Search by

omerkoseoglu / devextreme-data-laravel

omerkoseoglu

Laravel integration for omerkoseoglu/devextreme-data: server-side DevExtreme data processing over Eloquent models, query builders and collections.

Package info

github.com/omerkoseoglu/devextreme-data-laravel

pkg:composer/omerkoseoglu/devextreme-data-laravel

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-10-01 02:03 UTC

This package is auto-updated.

Last update: 2026-10-01 02:23:38 UTC


README

Packagist Version PHP Version CI Downloads License

Unofficial. This is an independent, community-maintained port. It is not affiliated with, endorsed by or supported by Developer Express Inc. "DevExtreme" and "DevExpress" are trademarks of Developer Express Inc.

Laravel integration for omerkoseoglu/devextreme-data: answer DevExtreme widget requests (DataGrid, PivotGrid, SelectBox, ... with remoteOperations) straight from Eloquent models, relations, query builders, collections or arrays. Filtering, sorting, paging, grouping and summaries run in the database.

Requires PHP 8.2+, Laravel 12 or 13. SQLite, MySQL/MariaDB and PostgreSQL connections.

Install

composer require omerkoseoglu/devextreme-data-laravel

The service provider and the DevExtreme facade are auto-discovered. Optional config:

php artisan vendor:publish --tag=devextreme-data-config

Usage

use DevExtreme\Data\Laravel\Facades\DevExtreme;

Route::get('/api/orders', fn () => DevExtreme::response(Order::class));

// any builder, relation or constraint
Route::get('/api/my-orders', fn (Request $r) =>
    DevExtreme::response($r->user()->orders()->where('status', 'open'))
);
const store = DevExpress.data.AspNet.createStore({ key: 'id', loadUrl: '/api/orders' });
new DevExpress.ui.dxDataGrid(el, { dataSource: store, remoteOperations: true });

DevExtreme::load($source) returns the LoadResult object (JSON-serializable) when you need to post-process it. Sources: model class-string, Eloquent\Builder, Relation, Query\Builder, Collection, array, or any DevExtreme\Data\Contracts\DataSourceInterface. Parameters are read from the query string, form or JSON body.

Malformed requests (bad JSON, mixed and/or, unknown field, ...) become HTTP 400 automatically.

How EloquentSource works

The builder is compiled to SQL — global scopes (soft deletes, tenancy), constraints and bindings included — and used as a derived table. DevExtreme operations are applied on top of it by the database. Consequences:

  • Your scopes and constraints always apply; clients cannot escape them.
  • Rows are returned as plain arrays: no model hydration, casts, accessors or $appends/$hidden. Select what you expose (->select('id', 'total')).
  • By default the model key is used as the stable-sort tie-breaker.

Exposing only some fields / joins

Pass a whitelist mapping client field names to output columns of your query. Dotted names become nested JSON:

use DevExtreme\Data\Laravel\EloquentSource;

$source = EloquentSource::for(
    Order::query()
        ->join('customers', 'customers.id', '=', 'orders.customer_id')
        ->select('orders.id', 'orders.total', 'customers.name as customer_name'),
    columns: ['id' => 'id', 'total' => 'total', 'customer.name' => 'customer_name'],
);

Route::get('/api/orders', fn () => DevExtreme::response($source));

Without columns, any plain column name of the query is accepted (identifier-checked, values are always bound). Use columns in production. Values may also be DB::raw() expressions: 'total' => DB::raw('amount * qty').

Configuration (config/devextreme-data.php)

Key Default Meaning
normalize_dates true Compare ISO-8601 filter dates as wall-clock time
max_take null Cap rows per request (protects against "load everything"). Leave null for PivotGrid endpoints

Request helper

$options = $request->devExtremeOptions(); // DevExtreme\Data\LoadOptions

Known limitations

  • No hydration. Rows are plain arrays: Eloquent casts, accessors, $appends, $hidden and model events do not run. Dates come back as strings, booleans as 0/1 on most drivers.
  • Queries bypass Laravel's database layer. EloquentSource runs on the connection's PDO directly, so DB::listen, the query log, Telescope and Debugbar do not see these queries. Transactions opened on the write connection are not visible if you use read/write splitting (the read PDO is used).
  • The builder is captured once when EloquentSource::for() is called; later changes to it are ignored.
  • Joins need explicit, unique column names (->select('orders.id', 'customers.name as customer_name')), because the query is used as a derived table.
  • Drivers: SQLite, MySQL/MariaDB, PostgreSQL. SQL Server is not supported.
  • Dates and timezones: filter dates are compared as wall-clock time and never converted; see the core package's known limitations (browser Date values reach the server as UTC).
  • Laravel 12 and 13 only (PHP 8.2+); older Laravel versions are not supported.

Development

composer install     # uses ../devextreme-php-data as a path repository
composer test

Tests run the core package's SQL-vs-memory parity matrix against EloquentSource, plus soft deletes, relations, joins, bindings and HTTP-level behaviour (Orchestra Testbench, SQLite in memory).

MIT licensed.