sergeil/extjsintegration-bundle
Symfony bundle that integrates ExtJS into your application, providing the glue between server-side Symfony code and ExtJS front-end components so you can build rich JavaScript interfaces with less boilerplate.
The first thing to establish is a framework mismatch: despite the "Laravel developer" framing, sergeil/extjsintegration-bundle is a Symfony bundle, not a Laravel package. There is no ServiceProvider, no config/extjs.php, and no auto-discovery. Your first job is deciding whether to bridge it into Laravel or lift the mapper/query-builder classes and drop the Symfony wiring.
Minimal steps:
DependencyInjection/, Resources/config/services.*, and the mapper/query-builder classes. The bundle's value is a small set of services; you are almost certainly not going to use the full Symfony kernel integration in a Laravel app.composer.json constraints. Try composer require sergeil/extjsintegration-bundle --ignore-platform-reqs in a scratch project first to see what it drags in (likely Doctrine ORM and specific Symfony components).store.proxy sends on read: page, start, limit, sort ([{property, direction}]), filter/filters (possibly nested and/or groups), and group. It returns a payload ExtJS expects (success, total, data). Verify this against your ExtJS version (4/5/6 formats differ slightly).If step 2 or 3 fails, stop — the integration cost will exceed rewriting the ~few hundred lines of param-to-query translation yourself.
Bridge via a Laravel ServiceProvider. Because the bundle expects a Symfony DI container, the cleanest path is a thin provider that instantiates the query-builder service manually (constructor-inject Doctrine's EntityManager or the bundle's connection abstraction if it has one) and binds it as a singleton. Treat the bundle as a library, not a bundle.
Controller-as-adapter. Keep ExtJS knowledge out of your domain. A pattern that holds up:
public function grid(Request $request, GridQuery $grid)
{
$result = $grid->for(User::class) // entity/table
->allow(['id', 'name', 'email', 'createdAt']) // whitelist
->fromRequest($request->all()) // page/start/limit/sort/filter
->paginate();
return response()->json([
'success' => true,
'total' => $result->total(),
'data' => $result->items(),
]);
}
Whitelist mapping for every column. ExtJS sends client-side field names; the bundle maps them to entity/column names. Maintain an explicit map (displayName => columnName) per grid rather than trusting sort[0].property. This is the single most important pattern — it is both a security boundary and your documentation of the API.
Support the filter-group shape early. ExtJS can send a flat filter array or nested filters with and/or logical groups. Write one normalizer that flattens both into an AST before handing it to the query builder, and test with an ExtJS-generated payload (capture it from the browser network tab) rather than hand-written JSON.
Reuse the response envelope. Extract the success/total/data shape into a small ExtJsResponse helper so every grid endpoint is consistent, including error responses (success: false, message: ...), which ExtJS will route to store.proxy.reader's exception handling.
Per-grid query objects. Wrap the bundle service in a small GridQuery class per resource. This gives you a place to add eager-loading (with) for relations shown as grid columns, and to keep the bundle's API from leaking across the codebase — useful if you later replace it.
For Eloquent projects, expect translation work. The bundle targets Doctrine. If your app is Eloquent-based, either (a) adopt Doctrine for the grids you expose, or (b) use the bundle only for param parsing/normalization and build the final query with Eloquent's orderBy/where from the normalized AST. Option (b) is usually more pragmatic and keeps the useful part.
symfony/* versions (Console, HttpFoundation, etc.). Resolve this before writing feature code; --ignore-platform-reqs is a smell, not a fix.total can be expensive. Count queries over large, joined, grouped result sets are the classic ExtJS-grid performance trap. Clone the query before applying LIMIT/OFFSET, avoid counting with joins you don't need, and consider an approximate count for very large grids.limit. ExtJS sends limit, but a hostile client can send limit=1000000. Cap it server-side (e.g., 200) inside your adapter, not in the bundle.sort[].property or filter field names into SQL/query-builder calls unvalidated. Validate against the whitelist and fail loudly — a where on an arbitrary column can become an authorization bypass.ASC/DESC; some query builders expect lowercase, and Doctrine/Eloquent differ in behavior on unknown directions. Normalize with strtoupper and a strict allowlist.Y-m-d\TH:i:s or a dateFormat-configured string). Convert to your DB format in the adapter; do not rely on the bundle to guess.page vs start/limit. Clients send both inconsistently; derive one from the other (start = (page-1)*limit) so paging never drifts by one.services.yaml decorates:/alias: rather than editing vendor code. A custom DataMapper implementation is the intended seam for non-standard field mapping.GridQuery abstraction and pin an exact version. With 0 dependents, upstream fixes are not coming; the abstraction is what lets you drop the package later.How can I help you explore Laravel packages today?