Skip to content

Coming from Lodata

If your Laravel app serves OData through flat3/lodata today, this page maps what you have onto laravelui5/odata. The protocol is the same, and so is the idea of turning Eloquent models into entity sets. What changes is the shape: a service class instead of a facade call, and an engine that only reads. Why this package exists at all is told in a post of its own.

1. Install and configure

bash
composer require laravelui5/odata
php artisan vendor:publish --provider="LaravelUi5\OData\ODataServiceProvider"

Most keys in config/odata.php carry the name you know from config/lodata.php:

config/lodata.phpconfig/odata.php
prefixprefixdefault odata, so clients keep their URL
middlewaremiddleware
streamingstreaming
namespacenamespace
versionversiondefault 4.0
pagination.max, pagination.defaultpagination.max, pagination.defaultdefault page size 200
readonlynonethe engine is always read-only
authorizationread_authorizera class instead of a gate, see §4
discovery.*nonethere is no discovery cache; the schema cache is odata:cache (Caching)
noneservice_registrywhich service answers a request, see §2

2. From discover() to a service class

In Lodata, models are discovered in a service provider:

php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
    \Lodata::discover(\App\Models\User::class);
}

Here they are bound in a service class, in configure():

php
namespace App\OData;

use App\Models\User;
use LaravelUi5\OData\ODataService;
use LaravelUi5\OData\Service\Contracts\EdmBuilderInterface;

class AppService extends ODataService
{
    public function serviceUri(): string
    {
        return '';
    }

    public function namespace(): string
    {
        return 'com.example.odata';
    }

    protected function configure(EdmBuilderInterface $builder): EdmBuilderInterface
    {
        $this->discoverModel(User::class);

        return $builder->namespace($this->namespace());
    }
}

An empty serviceUri() puts the service at the root of the prefix, /odata/, where Lodata served its model. Entity sets are named the same way, as the plural of the model: User becomes Users.

Where Lodata used one of its own entity set drivers (a SQL query, a collection, a data source of your own), configure() takes a custom entity set:

php
$this->discoverCustomEntitySet(\App\OData\ActiveCustomers::class);

How to write one, from a plain SQL view to a fully custom source, is on custom entity sets.

Finally, tell the package which service answers. The default registry serves an empty service, so a registry of your own is required, even for one service:

php
namespace App\OData;

use LaravelUi5\OData\Service\Contracts\ODataServiceInterface;
use LaravelUi5\OData\Service\Contracts\ODataServiceRegistryInterface;

class ServiceRegistry implements ODataServiceRegistryInterface
{
    public function resolve(string $fullPath): ODataServiceInterface
    {
        return new AppService();
    }

    public function services(): array
    {
        return [new AppService()];
    }
}
php
// config/odata.php
'service_registry' => App\OData\ServiceRegistry::class,

A registry that returns different services for different paths is how several services share one app; see multiple services.

3. Keeping fields out

Lodata leaves out a list of sensitive property names during discovery (discovery.blacklist). Here there is no list; you decide per model. Eloquent's $hidden keeps a column's value out of every response, but the column still appears in $metadata. To keep it out of the schema as well, mark it #[ODataIgnore], or give the set an entity type of its own with only the columns you want. Both are on model discovery.

4. Authorization

Lodata asks a gate named lodata about every request once authorization is switched on. Here a read authorizer is bound in config/odata.php (read_authorizer). It sees the parsed query plan and records a verdict per entity set: allow, deny the request, or drop an $expand the actor may not see. The contract and an example are on read authorization.

5. Writes

With readonly switched off, Lodata creates, updates and deletes entities. laravelui5/odata does not: PUT, PATCH, DELETE and every POST outside $batch are refused. Writes stay in your application, in controllers or actions with their own validation. With LaravelUi5 Core, a write is a Ui5Action.

6. Functions

Lodata's operations become functions: declared on the builder in configure() and bound to a resolver in bindFunctions(). Actions, the writing kind of operation, are not supported. See functions and singletons.

7. What does not come across

  • $apply, the aggregation extension. A request with it is answered with 501.
  • Asynchronous requests.
  • An OpenAPI document.
  • Connection files for Excel and Power BI. Both connect with the service URL instead; see Excel and Power BI.

8. Check it

Point your client at the new $metadata and compare it with Lodata's: entity set names, property types, keys. Then run the requests your client sends. The query options ($filter, $select, $expand, $orderby, $top, $skip, $count, $search, $compute) are each documented under Query Options.