it-bens/api-platform-utilites-bundle
A Symfony bundle offering utility helpers for API Platform projects. It bundles reusable services and tools to simplify common API Platform setup and customization tasks, reducing boilerplate in your PHP applications.
This is a pre-1.0 package (v0.1.1), so treat the API surface as unstable and pin an exact version in composer.json ("vendor/package": "0.1.1") rather than using a caret constraint.
Minimal setup:
composer require vendor/package — Laravel package discovery registers the service provider automatically.php artisan vendor:publish --tag=api-platform-config (check vendor:publish output for the exact tag).php artisan optimize:clear.Where to look first:
config/api-platform.php (or whichever config the package publishes) — routing prefix, serialization groups, and transformer registration behavior live here.ApiInputTransformer and ApiOutputTransformer in the package source. These are the two classes you will interact with on almost every request.context['resource_class'] value is what drives the output transformer, so the resource definition is the real entry point.First use case — expose an Eloquent model through API Platform:
#[ApiResource]
class UserOutput
{
public int $id;
public string $name;
public string $email;
}
The ApiOutputTransformer maps your App\Models\User into UserOutput whenever the request context identifies UserOutput as the resource_class. Once that round-trips, add a write path with ApiInputTransformer (an input DTO that becomes model attributes on POST/PATCH). That read-then-write loop is the core of the package; everything else is refinement.
Output transformation (read path). Keep the output DTO dumb — public typed properties, no behavior. Put the mapping logic in the transformer or a dedicated mapper the transformer delegates to. The transformer receives the Eloquent model plus a context array; branch on context['resource_class'] when one transformer serves multiple resources, or register a transformer per resource and keep them flat.
Input transformation (write path). ApiInputTransformer gives you a validated DTO rather than raw request input. Treat the DTO as the contract: validate in the DTO/form request, then let the transformer hydrate the model. Avoid letting the transformer reach back into request() — pass everything through the context so the transformer stays unit-testable.
Let auto-registration do the work. As of v0.1.1 the transformers are tagged api_platform.data_transformer, so API Platform's compiler pass picks them up. You should not need to tag, bind, or list them manually. If your app has a service provider doing that, delete it — see the gotcha below.
Custom transformers alongside the defaults. Register your own transformer and tag it the same way (->tag('api_platform.data_transformer') in a service provider, or the attribute equivalent). API Platform resolves by supported input/output class, so a transformer declaring a narrower supports()/getSupportedTypes() wins over the generic one.
Testing pattern. Two layers, both cheap:
resource_class behavior is easiest to pin down.$this->getJson('/api/users/1') and assert the JSON-LD shape (@id, @type, hydra:member). This catches registration problems that unit tests never will.Collections and relations. Test a collection endpoint early, not just a single item — transformer registration problems often surface only on the collection path, and nested resource embedding is where serialization groups bite.
resource_class is now a hard requirement — and it fails silently. The v0.1.1 fix makes ApiOutputTransformer check for resource_class in the context. On the HTTP path API Platform populates it for you. But if you ever invoke the transformer manually — a console command, a queued export, a unit test — you must build the context yourself:
$transformer->transform($user, ['resource_class' => UserOutput::class]);
Omit it and you get no transformation rather than an exception, which is a genuinely annoying thing to debug. If you were relying on the old behavior of transforming blindly, audit any manual calls before upgrading.
Remove manual transformer registration. If a previous version required you to tag or bind ApiInputTransformer/ApiOutputTransformer yourself, that registration is now redundant. Duplicate registrations lead to ambiguous resolution and, depending on ordering, the wrong transformer being picked. Grep your service providers for api_platform.data_transformer and drop what you added.
Tag changes are compile-time. Service tags are resolved when the container is compiled. After upgrading to 0.1.1 run php artisan optimize:clear and, in production, php artisan config:cache && php artisan route:cache as part of your deploy — otherwise a cached container will keep the old, untagged wiring and you will conclude the release did nothing.
Debugging registration. php artisan debug:container --tag=api_platform.data_transformer will show exactly which transformers API Platform sees. If the list is empty, the tag never applied (cached container, or a service provider that isn't loading). If it lists duplicates, you have leftover manual registration.
Debugging a "no transformation" response. Walk the chain in order: is the transformer in the tagged list → does it support your output class → is resource_class set in the context → is a serialization group excluding the property. The first two are install problems, the last two are code problems.
Version discipline. This is 0.1.x. Patch releases here are already changing registration behavior and transformer contracts, which is exactly the kind of thing that would be a minor bump post-1.0. Pin exact versions, read every release note, and keep a smoke test on a single GET and a single POST endpoint so an upgrade can't quietly break the wire format.
Extension points worth knowing. The transformer itself is the primary seam — extend it rather than editing vendor code, and override the mapping method. Config is the secondary seam for routing/serialization defaults. There is no event layer to speak of at this maturity, so don't plan an architecture that depends on one.
How can I help you explore Laravel packages today?