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 Symfony Laravel Package

omerkoseoglu/devextreme-data-symfony

A Symfony package for integrating DevExtreme DataSource with Symfony applications. It helps serve data to DevExtreme grids and components, simplifying backend wiring for common data operations like filtering, sorting, and paging so front-end widgets work with Symfony-powered APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

What you're adopting. omerkoseoglu/devextreme-data-symfony is the server-side half of a DevExtreme Data Grid / Data Source integration: the browser sends a loadOptions object (filter, sort, skip, take, requireTotalCount, group, summary) and the package turns it into a query plus the { data, totalCount, summary, groupCount } envelope DevExtreme expects back. Note the name: it is a Symfony package. Read the first 20 lines of composer.json before anything else and confirm the symfony/* constraints — in a Laravel app you will be using it as a standalone library with a thin bridge, not as an installed bundle.

Install and pin. v0.1.0 is a first release; do not float the constraint.

composer require omerkoseoglu/devextreme-data-symfony:0.1.*

Where to look first, in order:

  1. README.md — the canonical example. It will show the request-to-response call and the exact return shape; everything else is inference until you've matched it.
  2. CHANGELOG.md — currently a single "first release" entry. That absence of history is itself information: treat the public API as unproven.
  3. composer.json — PHP version floor, PSR-4 namespace, whether Doctrine ORM/DBAL or a generic query builder is the dependency. This single file tells you whether the package can plug into Eloquent at all.
  4. src/ — find the entry-point class that accepts an array of load options and the class that builds the response. Those two are your entire integration surface.
  5. tests/ — the fastest, most accurate spec of the filter grammar the package actually supports. Read the fixtures; they are real loadOptions payloads.

First use case. One endpoint, one grid, no grouping, no summaries: a "Customers" list with a text filter, one sortable column, and server-side paging. Get totalCount correct before adding anything else — it is the single most common place these integrations break in production.

// Shape only — verify names against the README.
Route::post('/api/customers/load', function (Request $request) {
    $options = $request->input('loadOptions', []);

    $provider = new DataProvider(Customer::query());   // adapter per README
    $result   = $provider->load($options);

    return response()->json([
        'data'       => $result['data'],
        'totalCount' => $result['totalCount'],
    ]);
});

Implementation Patterns

Isolate the package behind your own interface. With a 0.x dependency from a single maintainer, the correct architectural posture is a one-file seam:

interface GridDataProvider {
    public function load(array $loadOptions): array;
}

Everything in your app talks to GridDataProvider; only the implementation class mentions the package namespace. When v0.2.0 renames things, you edit one file.

Separate the two concerns the package conflates. DevExtreme integration is really (a) parsing untrusted client options and (b) applying them to a query. Pattern: parse into your own value objects (Filter, Sort, Page) first, validate the field whitelist, then hand off. This keeps the third-party types out of your domain and gives you a place to log/replay bad payloads.

Field allowlisting as a first-class config. Maintain a per-endpoint map of client-visible field names to real columns and casts, and reject unknown names with a 422 rather than letting a filter reach a where on an arbitrary column:

protected array $sortable = [
    'name'       => 'customers.last_name',
    'createdAt'  => 'customers.created_at',
];

Every DevExtreme backend integration in the wild needs this map; the package will not supply it for you.

Eloquent vs Doctrine. If the package ships a Doctrine query adapter only, the pragmatic patterns are, in order of preference:

  • Write a small adapter implementing whatever query interface the package defines, backed by Illuminate\Database\Query\Builder. Do this if the interface is narrow (it usually is: apply filter, apply sort, apply limit, count).
  • Run the package's parsing layer only, and apply the resulting filter tree yourself. This is the best risk/effort ratio and is worth checking for early — see whether the parse step is exposed independently of the execution step.
  • Fall back to in-memory filtering for small, cached datasets. Fine under a few thousand rows, and it is a legitimate way to ship v1 of a screen.

Wrap the endpoint in a feature flag / route toggle. Because the package is new, ship the grid behind a flag and keep the previous table working until the endpoint has survived real traffic.

Test with recorded payloads. Capture the actual JSON DevExtreme posts (browser devtools → network → request body) and commit those as fixtures. Assert on both data and totalCount. This is far more valuable than testing your own hand-written options arrays, because DevExtreme's filter nesting (["and", [...], ["or", ...]]) is idiosyncratic and only real payloads catch regressions.

Cache the count query. requireTotalCount means every page request runs a second, unpageable COUNT. For any non-trivial table, cache the count keyed on the filter subtree (not the sort/skip/take) with a short TTL.

Gotchas and Tips

This is Symfony, not Laravel — budget for the bridge. Expect friction at three specific points: the HTTP request type (Symfony Request/ParameterBag vs Illuminate\Http\Request), the DI story (Symfony bundle config vs a Laravel service provider you must write yourself), and configuration loading (YAML vs config/*.php). None are hard, all are undocumented for your target framework. Test the request-handling path first — if the entry point type-hints Symfony's Request, you'll be adapting at the boundary.

v0.1.0 means the public API is a draft. Pin 0.1.* at most, read the diff of every patch before bumping, and do not expose package classes in your own public signatures. There is no deprecation runway to rely on.

Trust nothing from the client. Field names, operators, and take values all arrive as untrusted input. Clamp take to a maximum (DevExtreme happily sends huge values), reject unknown operators, and never construct a column name by string concatenation.

DevExtreme contract quirks to verify against the package's tests:

  • When grouping is active, skip/take may be absent or zero — the grid often pages groups client-side. Don't assume paging is always server-side.
  • requireTotalCount and requireGroupCount are separate flags; returning totalCount unconditionally is wasteful, omitting it when required breaks the pager.
  • Filters mix string and numeric comparisons against the same logical field; date filters arrive as ISO strings. Type coercion must come from your field map, not from the payload.
  • Summary/groupSummary definitions are an array of aggregator/field pairs — a package at v0.1.0 may implement only count, sum, min, max, avg. Confirm before promising totals in the UI.
  • sort can be multi-column; ensure stable ordering by appending a deterministic tiebreaker (usually the primary key) or paging will duplicate and skip rows.

Debugging. When the grid renders wrong data, the bug is almost always in the payload, not the package. First step: log the raw loadOptions server-side and compare to what DevExtreme docs say that grid config should produce. Second step: log the generated query/SQL. Third: verify the JSON encoding — DateTime objects and decimals serialized as strings/objects instead of ISO/numeric will silently break sorting and formatting on the client.

Extension points to locate early. Look for (a) a pluggable query adapter or interface, (b) a filter-operator registry you can add to for custom/database-specific operators (full-text MATCH, JSON path contains), and (c) a response formatter or an event hook before serialization. Even at v0.1.0, one of these three usually exists, and each saves you a fork.

Bus factor. Single maintainer, first release, Symfony naming under a Laravel review question — evaluate it as a reference implementation of the DevExtreme load-options contract as much as a dependency. If the code is small, vendoring it (copy into your app, keep the license header) is a defensible call and removes the version-drift risk entirely.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect