Skip to content

Service Locator Enhancement #860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: 5.x
Choose a base branch
from
Open

Conversation

mgerzabek
Copy link
Contributor

Add Support for Modular OData Service Endpoints

This PR introduces a powerful mechanism for defining modular, multi-endpoint OData services in Laravel when using flat3/lodata. It enables a clean and scalable way to expose different domains or bounded contexts under separate OData endpoints, each with their own $metadata and query surface.


Key Features

  • Multiple service endpoints via configuration (config/lodata.php)
  • Each endpoint has:
    • Its own URI segment (/odata/{key}/...)
    • Its own namespace in $metadata
    • A discover() method for dynamic schema registration
    • Optional pre-generated metadata via cachedMetadataXMLPath()
  • Built-in performance optimization:
    • discover() is only invoked on-demand
    • No overhead for regular Laravel routes

Implementation Overview

  • Introduced ServiceEndpointInterface
  • Added dynamic endpoint registration logic to ServiceProvider
  • Endpoint resolution via URI prefix (based on config('lodata.endpoints'))
  • Schema discovery per endpoint
  • Optional static metadata XML serving ($endpoint->cachedMetadataXMLPath()) to support arbitrary annotations

Documentation

For a detailed overview of the architecture, configuration, and usage of modular service endpoints, please refer to the comprehensive documentation accompanying this pull request.

Benefits

  • Clean separation of API domains (e.g., HR, Finance, Projects)
  • Flexible architecture: mix static and dynamic metadata
  • Optimized performance (no eager booting of OData models)
  • No need for extra route definitions
  • Easy to scale and maintain

Notes

This PR does not introduce breaking changes. Existing single-endpoint behavior remains the default. If config('lodata.endpoints') is empty, the global service continues to be served as before.


Let me know if you'd like this split into smaller commits or if you'd prefer to review feature-by-feature. Thanks for the great base package! 🙌

@27pchrisl
Copy link
Contributor

@mgerzabek could you rebase on 5.x, I fixed the issue with the tests.
Also, please revert the .gitignore that removes the .idea folder - that folder is supposed to be there (https://www.jetbrains.com/help/phpstorm/creating-and-managing-projects.html)

Thanks!

@mgerzabek
Copy link
Contributor Author

@27pchrisl, I've rebased on 5.x and removed the .idea from .gitignore. Is there anything else that I could do?
Please let me know.

@27pchrisl
Copy link
Contributor

I've opened doctrine/dbal#7050 which should resolve the issue with the tests. @mgerzabek

mgerzabek and others added 25 commits July 22, 2025 14:48
- added missing type PropertyPath
- added Identifier to Record
- added missing Type attribute to Record
- added alias to Identifier – including dirty hack to derive it from given namespace
- added addReference to Lodata Facade to allow adding additional vocabularies to the model
- added alias for all auto registered References
This is important to allow Token Handling for Clients like OpenUi5
When multiple Endpoints are declared, and the frontend requests the global Endpoint (with only one path segment), accessing $segments[1] caused an 'Undefined array key' warning.

This commit adds a count($segments) check to avoid invalid index access and preserve compatibility with global Endpoint logic.
…t routing docs

- Introduced new method serviceUri(): string to explicitly declare the logical URI segment for each OData endpoint
- Updated ServiceEndpointInterface PHPDoc to reflect modular, config-driven routing
- Clarified semantics of endpoint() and route() to match actual behavior in Endpoint base class
- Aligns terminology with ServiceProvider::boot() logic for endpoint resolution
- remove "extra.laravel" from composer.json to disable auto-registration
- always boot default Endpoint in ServiceProvider for consistent behavior
- drop request path inspection logic for segmented endpoint resolution

==> apps requiring multiple OData endpoints should now implement and register
   their own ServiceProvider (see docs for example)
- added missing type PropertyPath
- added Identifier to Record
- added missing Type attribute to Record
- added alias to Identifier – including dirty hack to derive it from given namespace
- added addReference to Lodata Facade to allow adding additional vocabularies to the model
- added alias for all auto registered References
When multiple Endpoints are declared, and the frontend requests the global Endpoint (with only one path segment), accessing $segments[1] caused an 'Undefined array key' warning.

This commit adds a count($segments) check to avoid invalid index access and preserve compatibility with global Endpoint logic.
…t routing docs

- Introduced new method serviceUri(): string to explicitly declare the logical URI segment for each OData endpoint
- Updated ServiceEndpointInterface PHPDoc to reflect modular, config-driven routing
- Clarified semantics of endpoint() and route() to match actual behavior in Endpoint base class
- Aligns terminology with ServiceProvider::boot() logic for endpoint resolution
- remove "extra.laravel" from composer.json to disable auto-registration
- always boot default Endpoint in ServiceProvider for consistent behavior
- drop request path inspection logic for segmented endpoint resolution

==> apps requiring multiple OData endpoints should now implement and register
   their own ServiceProvider (see docs for example)
Resolve compatibility issue with `NavigationProperty::generatePropertyValue`
after the introduction of targeted entity ID handling in upstream PR
flat3#858. Restores support for local extensions
without breaking the new `$requestedTarget` logic.
@mgerzabek
Copy link
Contributor Author

@27pchrisl — Sorry for the noise, but I just realized that the PR merged yesterday introduced new incompatibilities with mine. Those should be resolved now. However, for some reason, MongoDB is once again causing CI trouble — likely unrelated to this PR.

As announced, this version no longer uses Composer extra for auto-discovery. Instead, the original ServiceProvider was enhanced to eliminate the static route() and endpoint() methods. This brings more flexibility and aligns better with Laravel conventions.

The documentation has been updated accordingly, including a new example of a multi-endpoint-capable service provider for those who need modular setups.

Let me know if anything else needs adjustment — thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants