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

Devextreme Data Laravel Laravel Package

omerkoseoglu/devextreme-data-laravel

A Laravel package that bridges DevExtreme DataSource requests with Laravel and Eloquent, helping you power DataGrid and other DevExtreme widgets with server-side filtering, sorting, paging, and data loading through familiar Laravel query patterns.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the first public release (v0.1.0), so treat it as pre-stable: the API may change in 0.x minor versions, and the CHANGELOG is the authoritative record of what landed.

  • Install: composer require omerkoseoglu/devextreme-data-laravel. The service provider is auto-discovered; publish the config with php artisan vendor:publish --tag=devextreme-data-config (check vendor/omerkoseoglu/devextreme-data-laravel/config/ for the exact tag if that one is missing).
  • Look here first: (1) the package config/ file, for defaults around paging, allowed filter columns, and query-parameter names; (2) the src/ directory, for the filter-parser and the DataSource/response builder — those two classes are what you will actually interact with; (3) CHANGELOG.md and the README examples on the GitHub repo, which are still the primary docs at this version.
  • First use case: you have a DevExtreme DataGrid (or TreeList/Lookup) pointing at a Laravel endpoint, and you are currently hand-rolling skip/take/sort/filter parsing in the controller. Replace that with the package's DataSource so DevExtreme's load payload — filter expressions, multi-column sort, grouping, summaries, and requireTotalCount — is translated into Eloquent/Query Builder calls for you.

A minimal flow: create a controller method, wrap your base query (Model::query() or a report query) in the package's DataSource, return the JSON response, and point the widget's dataSource.store at that route with remoteOperations: true.

Implementation Patterns

One DataSource class per grid. Rather than stuffing parsing into controllers, define a small class or method per widget that owns the base query and the column whitelist. Controllers stay thin and the query is testable without HTTP.

Whitelist columns explicitly. DevExtreme sends column names as plain strings in sort and filter. Map incoming names to real columns/aliases (or reject them) before they reach the query builder. A $columns = ['id', 'name', 'created_at' => 'users.created_at'] map covers aliasing, joined tables, and safety in one place.

Translate the filter tree, don't regex it. DevExtreme filters arrive as nested arrays — ["col", "contains", "x"], [["a","=",1],"and",["b","<>",2]] — with operators like contains, notcontains, startswith, endswith, and !-prefixed negations. Let the package's parser walk the tree; if you must extend it, add operator cases to the parser rather than post-processing SQL strings.

Handle the CRUD trio separately from reads. With editing enabled, the widget POSTs { data: { key, values } } for updates and { data: { key } } for deletes to the same URL. Branch on the presence of data/values in the request and validate with a FormRequest before touching the model — the package handles query translation, not your authorization or validation rules.

Master-detail and Lookup grids. Child grids send their own filter (usually ["fk_id", "=", parentId]) on load. Because the DataSource is stateless per request, the same class serves both parent and detail endpoints — just vary the base query or the column whitelist.

Computed columns. For totals or derived fields, selectRaw the expression and expose it under a stable alias, then add that alias to the whitelist. Keep the alias identical to the dataField in the widget config so sorting and filtering on it work without extra mapping.

Summaries and grouping. totalSummary and groupSummary come as arrays of { selector, summaryType } where summaryType is sum|avg|min|max|count. Map these to aggregate selects; for grouped loads, remember the response shape must be { key, items, count, summary } per group, not a flat list.

Eager loading. Apply with() on the base query before handing it to the DataSource so relation-backed columns don't trigger N+1 across the page of results.

Testing. Assert against the generated query (toSql()/getBindings()) or the decoded JSON for a canned request payload. Keep fixtures of real DevExtreme request bodies captured from the browser — they are the fastest regression suite for filter edge cases.

Gotchas and Tips

  • take: 0 means "all rows." DevExtreme sends 0 rather than omitting take when paging is off. Treat it as unbounded, and guard the endpoint with a hard cap so a stray request can't serialize a million rows.
  • requireTotalCount is not free. The widget asks for a count on nearly every load, and counting a grouped/distinct/joined query can cost more than the page itself. Cache counts keyed on the entire filter+group payload, or set requireTotalCount: false on the store where the UI tolerates it.
  • Filter shape is arrays, not objects. DevExtreme never sends { field, op, value } — it's positional arrays, recursively nested. Code that assumes keys will break on the second and/or.
  • Negation is encoded in the operator string ("!=", "notcontains"), not as a separate flag. Normalizing !-prefixed ops to a positive op plus a NOT is the cleanest fix if you extend the parser.
  • Dates arrive as ISO strings and are compared in the DB's timezone. If you store UTC and the user filters by local day, between filters will drift by hours. Normalize on the boundary or document the assumption.
  • Joins multiply rows. A hasMany join plus skip/take gives you wrong pages, not an error. Prefer whereHas/subqueries for filtering on relations; keep the main query single-row-per-entity.
  • avg over zero rows returns null, not 0. Coalesce in the summary mapping or the grid renders blank cells that look like a bug.
  • Debugging: log the incoming request payload and the final SQL with bindings at debug level. If a grid renders oddly, temporarily set remoteOperations: false to confirm the widget config is right before blaming the server. The browser Network tab shows the exact payload the parser is consuming.
  • Config quirks: the package config likely controls the request parameter names and default paging. If your frontend uses a custom load adapter, adjust the config rather than renaming fields in the controller.
  • Extension points: the filter parser and the response builder are the two seams worth subclassing. Bind your subclass in a service provider rather than editing vendor/ — this is 0.1.0, and a composer update will overwrite forks.
  • Pin the version. With no prior stable API, use "omerkoseoglu/devextreme-data-laravel": "0.1.*" and read CHANGELOG.md before bumping; a 0.x minor bump may rename config keys or response helpers.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource