PHPackages                             novabytes/laravel-odata - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. novabytes/laravel-odata

ActiveLibrary[Database &amp; ORM](/categories/database)

novabytes/laravel-odata
=======================

Apply OData 4 query options to Eloquent models. Supports $filter, $select, $expand, $orderby, $top, $skip, and $count.

v0.3.0(3mo ago)09MITPHPPHP ^8.2CI passing

Since Mar 20Pushed 3mo agoCompare

[ Source](https://github.com/novabytes-labs/laravel-odata)[ Packagist](https://packagist.org/packages/novabytes/laravel-odata)[ RSS](/packages/novabytes-laravel-odata/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (4)Dependencies (14)Versions (5)Used By (0)

Laravel OData
=============

[](#laravel-odata)

[![Latest Version on Packagist](https://camo.githubusercontent.com/e0c45df7d931361d37269f1677a56562cde3c7ffb3b4efa320518ed5abae881a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e6f766162797465732f6c61726176656c2d6f646174612e737667)](https://packagist.org/packages/novabytes/laravel-odata)[![Test Status](https://camo.githubusercontent.com/5663d321af4844012ecdea54a7c6bc2b35dc81b37228ceaa57b77f5ff594d228/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e6f766162797465732d6c6162732f6c61726176656c2d6f646174612f63692e796d6c3f6c6162656c3d7465737473266272616e63683d6d6173746572)](https://camo.githubusercontent.com/5663d321af4844012ecdea54a7c6bc2b35dc81b37228ceaa57b77f5ff594d228/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e6f766162797465732d6c6162732f6c61726176656c2d6f646174612f63692e796d6c3f6c6162656c3d7465737473266272616e63683d6d6173746572)[![Code Style Status](https://camo.githubusercontent.com/9c6d72afbfa83bbd2d1bf7458c10c79c3a519bd8bfe1ff2c44452b69384fdf45/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e6f766162797465732d6c6162732f6c61726176656c2d6f646174612f63692e796d6c3f6c6162656c3d636f64652532307374796c65266272616e63683d6d6173746572)](https://camo.githubusercontent.com/9c6d72afbfa83bbd2d1bf7458c10c79c3a519bd8bfe1ff2c44452b69384fdf45/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e6f766162797465732d6c6162732f6c61726176656c2d6f646174612f63692e796d6c3f6c6162656c3d636f64652532307374796c65266272616e63683d6d6173746572)[![Total Downloads](https://camo.githubusercontent.com/3b05339fb3fd40ef84f477c3d8f3108d20668c960dd8235747919fa4a00caa41/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e6f766162797465732f6c61726176656c2d6f646174612e737667)](https://packagist.org/packages/novabytes/laravel-odata)

OData 4 for Laravel. Query (filter, select, expand, sort, paginate) and CRUD (create, read, update, delete) your Eloquent models via OData-compliant endpoints.

Built on top of [novabytes-labs/odata-query-parser](https://github.com/novabytes-labs/odata-query-parser).

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [CRUD Endpoints](#crud-endpoints)
- [Query Options](#query-options)
- [PascalCase Conversion](#pascalcase-conversion)
- [Security](#security)
- [Entity Sets &amp; Metadata](#entity-sets--metadata)
    - [Metadata Endpoints](#metadata-endpoints)
- [Configuration](#configuration)
- [Advanced Usage](#advanced-usage)
- [Requirements](#requirements)
- [License](#license)

Installation
------------

[](#installation)

```
composer require novabytes/laravel-odata
```

Publish the config file:

```
php artisan vendor:publish --tag=odata-config
```

Quick Start
-----------

[](#quick-start)

```
use NovaBytes\OData\Laravel\ODataQueryBuilder;

class ProductController extends Controller
{
    public function index(Request $request)
    {
        return ODataQueryBuilder::for(Product::class, $request)
            ->allowedFilters('name', 'price', 'is_active', 'category_id')
            ->allowedSorts('name', 'price', 'created_at')
            ->allowedExpands('category', 'reviews')
            ->allowedSelects('id', 'name', 'price', 'description', 'category_id')
            ->get();
    }
}
```

Your API now accepts OData queries:

```
GET /products?$filter=Price gt 100&$select=Name,Price&$expand=Category&$orderby=Price desc&$top=50&$skip=10&$count=true

```

CRUD Endpoints
--------------

[](#crud-endpoints)

Enable auto-registered CRUD routes for your entity sets:

```
// config/odata.php

'entity_sets' => [
    \App\Models\Product::class => [
        'entitySet'       => 'Products',
        'operations'      => ['read', 'create', 'update', 'delete'],
        'allowedFilters'  => ['name', 'price'],
        'allowedSorts'    => ['name', 'price'],
        'allowedExpands'  => ['category'],
        'allowedSelects'  => ['id', 'name', 'price', 'description'],
        'allowedCreates'  => ['name', 'price', 'description', 'category_id'],
        'allowedUpdates'  => ['name', 'price', 'description'],
    ],
],

'crud' => [
    'enabled'            => true,
    'route_prefix'       => 'api',
    'middleware'          => ['api'],
    'default_operations' => ['read'],
],
```

This registers the following routes:

MethodRouteDescription`GET``/api/Products`List with OData query options`POST``/api/Products`Create entity`GET``/api/Products/{key}`Get single entity`PUT``/api/Products/{key}`Full replace`PATCH``/api/Products/{key}`Partial update`DELETE``/api/Products/{key}`Delete entityRequest bodies accept both PascalCase and snake\_case field names. Only fields listed in `allowedCreates`/`allowedUpdates` are accepted — others return `400`.

The CSDL and OpenAPI metadata endpoints automatically reflect CRUD capabilities.

Query Options
-------------

[](#query-options)

OptionExampleDescription`$filter``Price gt 100`, `contains(Name,'Widget')`, `Reviews/any(r:r/Rating gt 4)`Filter results using comparison operators, functions, and lambda expressions`$select``Name,Price`Choose which properties to return (primary key always included)`$expand``Category`, `Reviews($filter=Rating gt 4;$top=5)`Eager-load relationships with optional nested query options`$orderby``Price desc`, `Name asc,Price desc`Sort results by one or more properties`$top``10`Limit the number of results`$skip``20`Skip a number of results (for pagination)`$count``true`Include total count in the responseAll OData 4 comparison operators (`eq`, `ne`, `gt`, `ge`, `lt`, `le`), logical operators (`and`, `or`, `not`), and 30+ built-in functions are supported. See the [parser README](https://github.com/novabytes-labs/odata-query-parser) for the full list.

PascalCase Conversion
---------------------

[](#pascalcase-conversion)

OData uses PascalCase property names. This package automatically converts them to Eloquent's snake\_case:

ODataEloquent`Price``price``CategoryId``category_id``IsActive``is_active``Category` (expand)`category` (relationship)Define your allowlists in snake\_case — the conversion is handled for you.

Security
--------

[](#security)

Every filterable, sortable, expandable, and selectable field must be explicitly whitelisted. Any request for a non-whitelisted field throws a `400 Bad Request` by default.

If no allowlist is set for a given operation, that operation is unrestricted.

Entity Sets &amp; Metadata
--------------------------

[](#entity-sets--metadata)

Register your models and their allowlists centrally in `config/odata.php` to enable:

- Auto-generated `$metadata` (CSDL XML) endpoint for OData clients
- Auto-generated OpenAPI 3.0 spec for human-readable documentation
- Shared allowlists — no need to repeat `allowedFilters()` etc. in every controller

```
// config/odata.php

'entity_sets' => [
    \App\Models\Product::class => [
        'entitySet'       => 'Products',           // optional, auto-generated from table name
        'operations'      => ['read', 'create', 'update', 'delete'],
        'allowedFilters'  => ['name', 'price', 'is_active'],
        'allowedSorts'    => ['name', 'price', 'created_at'],
        'allowedExpands'  => ['category', 'reviews'],
        'allowedSelects'  => ['id', 'name', 'price', 'description'],
        'allowedCreates'  => ['name', 'price', 'description', 'category_id'],
        'allowedUpdates'  => ['name', 'price', 'description'],
    ],
    \App\Models\Category::class => [
        'operations'      => ['read'],
        'allowedFilters'  => ['name'],
        'allowedExpands'  => ['products'],
    ],
],

'namespace' => 'Default',

'metadata' => [
    'enabled' => true,
    'route_prefix' => 'odata',
    'openapi' => [
        'title' => 'My OData API',
        'version' => '1.0.0',
        'description' => '',
    ],
],
```

When `entity_sets` are configured, controllers can omit explicit allowlists:

```
// Allowlists are loaded from config automatically
ODataQueryBuilder::for(Product::class, $request)->get();

// Explicit calls still override config when you need to restrict further
ODataQueryBuilder::for(Product::class, $request)
    ->allowedFilters('name')  // overrides config for this endpoint
    ->get();
```

### Metadata Endpoints

[](#metadata-endpoints)

When `metadata.enabled` is `true`, two routes are registered automatically:

EndpointContent-TypeDescription`GET {prefix}/$metadata``application/xml`OData v4 CSDL document — used by Power BI, Excel, and OData client libraries`GET {prefix}/openapi.json``application/json`OpenAPI 3.0 spec — use with Swagger UI, Redoc, or any API docs toolBoth are auto-generated from your Eloquent models and the `entity_sets` config. Zero annotations needed — columns, types, and nullability are discovered from the database schema; relationships are discovered via reflection.

Configuration
-------------

[](#configuration)

```
// config/odata.php

return [
    'response_format'  => 'laravel',  // 'laravel' or 'odata'
    'max_expand_depth' => 3,          // Max $expand nesting depth
    'max_top'          => 1000,       // Max $top value (null = unlimited)
    'default_top'      => null,       // Default $top when not specified (null = no limit)
    'throw_on_invalid' => true,       // true = 400 on invalid ops, false = silently ignore
    'entity_sets'      => [],         // Model registrations (see above)
    'namespace'        => 'Default',  // CSDL schema namespace
    'metadata'         => [           // Metadata endpoint config
        'enabled'      => false,
        'route_prefix' => 'odata',
        'openapi'      => ['title' => 'OData API', 'version' => '1.0.0', 'description' => ''],
    ],
    'crud'             => [           // CRUD endpoint config
        'enabled'            => false,
        'route_prefix'       => 'api',
        'middleware'          => ['api'],
        'default_operations' => ['read'],
    ],
];
```

Advanced Usage
--------------

[](#advanced-usage)

### Using `toBuilder()`

[](#using-tobuilder)

```
$builder = ODataQueryBuilder::for(Product::class, $request)
    ->allowedFilters('price')
    ->toBuilder();

$results = $builder->where('is_active', true)->get();
```

### Existing query as starting point

[](#existing-query-as-starting-point)

```
$query = Product::where('is_active', true);

$results = ODataQueryBuilder::for($query, $request)
    ->allowedFilters('name', 'price')
    ->get();
```

Requirements
------------

[](#requirements)

- PHP &gt;= 8.2
- Laravel 11, 12, or 13

License
-------

[](#license)

MIT

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance82

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 94.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

94d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0c2ab9193e49e602acf43f6a345525a10675c2957cc8990fc1ac0978900307e6?d=identicon)[NovaBytes](/maintainers/NovaBytes)

---

Top Contributors

[![Eligioo](https://avatars.githubusercontent.com/u/8634939?v=4)](https://github.com/Eligioo "Eligioo (16 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

apilaraveleloquentqueryfilterodata

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/novabytes-laravel-odata/health.svg)

```
[![Health](https://phpackages.com/badges/novabytes-laravel-odata/health.svg)](https://phpackages.com/packages/novabytes-laravel-odata)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[kirschbaum-development/eloquent-power-joins

The Laravel magic applied to joins.

1.6k29.9M42](/packages/kirschbaum-development-eloquent-power-joins)[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k5.0M31](/packages/tucker-eric-eloquentfilter)[reedware/laravel-relation-joins

Adds the ability to join on a relationship by name.

2121.2M16](/packages/reedware-laravel-relation-joins)[mehdi-fathi/eloquent-filter

Eloquent Filter adds custom filters automatically to your Eloquent Models in Laravel.It's easy to use and fully dynamic, just with sending the Query Strings to it.

448196.7k1](/packages/mehdi-fathi-eloquent-filter)[api-platform/laravel

API Platform support for Laravel

59156.3k11](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
