[RFC] Resources defined outside of PHP code (runtime / external metadata sources)
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 30/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Ambito
- backend-api-design
Direzione di ricerca
Start by reading AbstractItemNormalizer, IriConverter, ResourceClassResolver, and the cached metadata factory implementations named in the issue. The proposal has unresolved design questions about resource identity, instantiation, and cache invalidation, so first agree on the direction with maintainers. Done means implementing the agreed runtime resource support and covering resource discovery, serialization, instantiation, and IRI behavior with tests.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Allow API resources declared from a source other than PHP code (a database, another API, a configuration
service) to work without a PHP class per resource. Discovery and metadata can already be provided by
decorating the metadata factories; what is missing are a few runtime hooks, because the serializer, the IRI
converter and the resource class resolver derive the resource from $object::class.
Use case
Administrators define content models at runtime (fields, types, relations) without deployment or migration.
We want each model to be a real API resource:
GET /dynamic/faq/{id}→"@type": "DynamicFaq", propertiesquestion,answer,theme…- per-model JSON Schema / OpenAPI / Hydra documentation
- per-model validation derived from field definitions
- IRIs generated and resolved like any other resource (
getIriFromResource,getResourceFromIri) - a new or modified model available without a deployment
How the data is persisted is up to the application (in our case a generic Doctrine entity with a JSON
column), exactly as with DTOs and stateOptions today.
Similar needs: headless CMS builders, form builders, multi-tenant schemas, schemaless document stores,
resources proxied from a remote API whose schema is discovered at runtime.
What already works
Decorating ResourceNameCollectionFactoryInterface, ResourceMetadataCollectionFactoryInterface (below the
cache) and the property factories lets an application declare resources, operations and properties from any
source. The Laravel Eloquent integration does it for properties. Types come from ApiProperty::nativeType /
schema, so no reflection is needed. Operations, routes and documentation can be generated this way.
What breaks
Once a resource is identified by a name that is not a class (DynamicFaq):
| Where | Code | Effect |
|---|---|---|
AbstractItemNormalizer::instantiateObject() |
new \ReflectionClass($class) |
ReflectionException on every write operation |
AbstractItemNormalizer::normalize() |
$context['force_resource_class'] ?? $this->getObjectClass($data) |
resource derived from the PHP class of the object |
IriConverter::getIriFromResource() |
same | wrong or missing IRI for nested relations, cache purge, Mercure |
ResourceClassResolver::getResourceClass() / isResourceClass() |
getObjectClass() + is_a() |
an object cannot belong to a resource that is not a class |
CachedResourceNameCollectionFactory, Cached*MetadataFactory |
no invalidation | a new resource is only visible after a cache clear |
Workarounds without core changes:
- Generate one PHP class per resource at runtime: code generation in production, opcache/container
rebuilds, multi-server synchronisation. - Map all resources onto one shared class:
getObjectClass()always returns that class, so the resource
of a nested object cannot be determined. - Decorate the normalizers,
IriConverterandResourceClassResolver: re-implementing part of the core
outside the project, fragile across upgrades.
Proposal
1. Objects carrying their resource
interface ResourceAwareInterface
{
public function getResource(): string;
}
When an object implements it, AbstractItemNormalizer, IriConverter and ResourceClassResolver use
getResource() instead of $object::class. Nested relations and getIriFromResource() outside the
operation context then work as for any resource.
Optionally, the core could ship a generic implementation for applications that do not want their own:
final class DynamicResource implements ResourceAwareInterface, \ArrayAccess, \IteratorAggregate
{
public function __construct(private readonly string $resource, private array $data = []) {}
public function getResource(): string { return $this->resource; }
}
2. Instantiation hook for name-identified resources
When the resource is not a class, the serializer delegates instantiation (and property read/write) instead
of calling new \ReflectionClass(): by default to DynamicResource, or to a class declared in the
resource metadata. The processor receives that object and maps it to the domain model, as DTOs with
stateOptions do today.
3. Metadata cache invalidation (optional)
Cache tags on the metadata pools + MetadataCacheInvalidatorInterface::invalidate(string $resource), so a
resource added or modified at runtime is picked up without a cache clear. Routing can rely on a catch-all
route or a router cache fingerprint, left to the application at first. GraphQL is out of scope.
Backward compatibility
No BC break: everything is opt-in and introduced through new interfaces and classes. Existing interfaces are
not modified, and objects that do not implement ResourceAwareInterface behave exactly as today. It can ship
in a 5.x minor, flagged experimental.
Known limitation: many extension points document string $resourceClass as class-string. Third-party code
assuming so (new \ReflectionClass($resourceClass), class_exists(), getRepository()) keeps working for
class-based resources but will not support name-identified resources. This should be documented for
extension authors.
Open questions
- Is a resource identity that is not a class name acceptable for the project?
ResourceAwareInterfaceonly, or also a genericDynamicResourceshipped by the core?- Resource name vs.
shortName: reuseshortNameas identity, or introduce a new key? - Is metadata cache invalidation in scope, or left to applications?
Contribution
We can implement the ResourceAwareInterface support first, then the instantiation hook, after agreement on
the direction. We can also contribute documentation on providing resources by decorating the metadata
factories.
- Lingua principale
- PHP
- Stelle
- 2.6k
- Fork
- 988
- Merge medio
- 4g 10h
- PR unite (30g)
- 92
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di api-platform/core
-
API Platform ignores array shapes in the JSON SchemaForse già presa @afraca l’ha presa 9 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
api-platform/core#8766 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
api-platform/core#8744 ·
I maintainer di solito rispondono entro 2 giorni
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`Forse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 Meno di un'ora Idoneità per principianti 80/100
api-platform/core#8701 ·
I maintainer di solito rispondono entro 2 giorni
-
Preserve exact SearchFilter behaviour for Doctrine collection associations with QueryParameterAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
api-platform/core#8790 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 45/100
api-platform/core#8785 ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di api-platform/core
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 74/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
Automattic/blocks-engine#4355 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
agentic bug complete-verify
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
MagnaCapax/PMSS#1234 ·
I maintainer di solito rispondono entro 3 giorni