Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Qbjs Parser Laravel Package

wlogic/qbjs-parser

A lightweight Laravel/PHP parser for QBJS (QuickBooks JSON) data. It converts accounting records into structured PHP arrays and objects, making validation, querying, and integration simple. Ideal for importing customers, invoices, and transactions.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and inspect the source before wiring it into anything:

composer require wlogic/qbjs-parser

Where to look first:

  • src/ — the parser class and any operator-mapping/config file. The public entry point is small; read it end to end before trusting the README examples.
  • tests/ (if present) — the best specification for operator names, nesting behaviour, and the shape of the returned object (SQL string + bindings, or a query object).
  • README — quickest confirmation of the return type. This package has no Laravel service provider, so you instantiate it yourself.

First use case — a filtered index endpoint that receives the jQuery QueryBuilder JSON and returns paginated rows:

Route::post('/orders/filter', function (Request $request) {
    $parser = new \WLogic\QBJSParser\Parser(); // confirm exact class in src/

    $result = $parser->parse($request->input('rules'));

    return DB::table('orders')
        ->whereRaw($result->getSql(), $result->getBindings())
        ->paginate(25);
});

The payload coming from the front end looks like:

{
  "condition": "AND",
  "rules": [
    { "field": "status", "operator": "equal", "value": "paid" },
    { "field": "total",  "operator": "greater_or_equal", "value": 100 },
    {
      "condition": "OR",
      "rules": [
        { "field": "country", "operator": "in", "value": ["DE", "FR"] },
        { "field": "vip",     "operator": "equal", "value": true }
      ]
    }
  ]
}

Start with a flat, single-level AND group, dump the generated SQL and bindings, and only then add nesting. That sequence catches operator-name mismatches in minutes instead of debugging a nested query.

Implementation Patterns

Wrap the parser in a single service. Don't call it from controllers directly. A RuleQueryService centralises the field whitelist, operator whitelist, and output composition, and gives you one place to swap parsers if the package stalls:

class RuleQueryService
{
    public function apply(Builder $query, array $rules): Builder
    {
        $result = (new Parser())->parse($rules);

        return $query->whereRaw($result->getSql(), $result->getBindings());
    }
}

Field whitelisting / aliasing. jQuery QueryBuilder sends whatever id you gave the field on the front end. That is not necessarily a column name. Keep an explicit map (['customer_name' => 'customers.name']) and reject anything not in it before parsing — this is both a security control and a way to support joined tables.

Compose with the base query. The parser has no idea about your tenancy, soft-deletes, or default scopes. Always apply the parser after those constraints so the user can only narrow an already-restricted result set:

$query = Order::where('tenant_id', $tenant->id)->whereNull('deleted_at');
$query = $service->apply($query, $request->input('rules', []));

Cache parsed output per request payload. The JSON rule tree is deterministic; hashing it and storing the SQL + bindings for a few minutes is a cheap win if the endpoint is hot, since parsing happens on every keystroke-triggered refresh from the UI.

Testing pattern. Keep a set of fixtures — one flat AND, one nested OR, one with in and between, one with an empty rule set — and assert against getSql() and getBindings() rather than running the query. Assert SQL structure loosely (normalise whitespace) so minor formatting changes don't break tests.

Front-end contract. Return the parsed rule tree back to the client so the UI can redisplay the active filters, and version the JSON shape in an Accept header or a ?v= query param if you expect the jQuery QueryBuilder config to change.

Feature flag the endpoint. With zero dependents and a single 2024 release, rollout behind a flag lets you switch back to hand-rolled where clauses if a specific operator misbehaves.

Gotchas and Tips

  • SQL injection via field. The rule's field name almost certainly flows into the SQL string. If the parser doesn't validate it, you must. Whitelist to known columns and never pass the raw value through.
  • Operator naming drift. jQuery QueryBuilder emits names like equal, not_equal, less, greater, less_or_equal, greater_or_equal, begins_with, contains, in, not_in, between, is_null, is_not_null. Confirm each is supported in the parser's operator map before advertising it in the UI, and hide unsupported operators client-side.
  • Type coercion. "value": "100" and "value": 100 behave differently across engines. Normalise types server-side based on the field's declared type (string/number/date/boolean) before handing the tree to the parser, especially for between and in.
  • Binding order with nested groups. Nested OR groups plus whereRaw can produce bindings in an order that doesn't match the SQL if you split the tree. Parse the whole tree in one call and pass the returned bindings verbatim.
  • Empty rule sets. A fresh QueryBuilder widget sends rules: [] with condition: "AND". Decide whether that means "no filter" or "return nothing" — most APIs want the former, so short-circuit on empty before calling the parser.
  • Null handling for is_null. The value key may be absent or null. Guard against Undefined index warnings, and remember whereRaw with a null binding produces LIMIT-like surprises on some drivers.
  • IN empty array. {"operator": "in", "value": []} is valid on the front end but renders IN (), which is a syntax error on MySQL. Replace with a 1=0 short-circuit.
  • Placeholder style. Check whether the parser emits ? or named placeholders. Laravel's whereRaw expects ?; if the package emits :name, you'll need to bypass the builder and use DB::select.
  • No service provider, no config publish. Everything is constructor-injected. If you need custom operator handling, look for a public operator-map setter or subclass with an overridden mapping method — that's your extension point.
  • Maintenance risk. One release (March 2024), zero stars, zero dependents. Treat the source as vendor code: read it, fork it into app/Support if you need to patch an operator, and pin the exact version in composer.json.
  • Debugging. dd($result->getSql(), $result->getBindings()) before execution is faster than reading stack traces — most failures are operator-name or column-name mismatches, not parser bugs.
  • Logging. Log the raw rule JSON alongside the request ID for failed filter requests; reproducing a user's exact filter state from a screenshot is painful otherwise.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route