Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Fos Rest Extra Bundle Laravel Package

m6web/fos-rest-extra-bundle

A Symfony bundle that extends FOSRestBundle with extra utilities for building REST APIs. It provides convenient configuration and helpers to reduce boilerplate around request handling and responses, integrating cleanly into existing FOSRestBundle-based applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require m6web/fos-rest-extra-bundle:^4.0

v4.0.0 is the Symfony 6-compatible line. That is the headline of the release and effectively the whole of it — there are no new features, no new config keys, and no documented deprecations, so the day-to-day usage is identical to v1. What changed is the framework floor: expect PHP 8.1+ and symfony/* ^6.x constraints. Verify the actual matrix rather than trusting the one-line changelog:

composer show m6web/fos-rest-extra-bundle --all   # requires / conflicts

Registration is unchanged, and there is still no Flex recipe, so do it by hand:

// config/bundles.php
M6Web\Bundle\FOSRestExtraBundle\M6WebFOSRestExtraBundle::class => ['all' => true],

The bundle sits on top of FOSRestBundle, so friendsofsymfony/rest-bundle (and typically jms/serializer-bundle or the Symfony serializer) must already be installed and working. If your API is not yet returning FOSRest View responses, stop and fix that first.

Where to look first

Documentation is still thin and the repo is the source of truth. Read these three files in the vendor directory in this order:

  1. README.md — the feature list and any usage examples.
  2. DependencyInjection/Configuration.php — the entire configuration tree, including the real config key (usually m6web_fos_rest_extra) and every option.
  3. Resources/config/services.xml — the service ids and, crucially, the priority attributes on any tagged event listeners. Listener priority is where most integration bugs come from.

Confirm what actually got wired after the v4 bump — this is the fastest way to catch a service that silently stopped being registered under the new Symfony version:

bin/console debug:container --tag=kernel.event_subscriber | grep -i m6web
bin/console debug:event-dispatcher kernel.request

First use case

The realistic entry point is unchanged: you have a working FOSRest controller returning View::create($entity, 200) and you want request-driven serialization context (groups/fields supplied by the client) plus the listener plumbing around it. Enable the bundle with defaults, hit one existing endpoint, and diff the response against what you had before. With v4 specifically, do this immediately after the Symfony 6 upgrade and before any other refactoring — you want the bundle's delta isolated in the diff.

Implementation Patterns

Keep FOSRest as the skeleton. Don't rewrite controllers to "use" this bundle. Continue with AbstractFOSRestController and View objects — on Symfony 6 that means PHP 8 attributes, #[Rest\Get('/api/pages/{id}')], not the old docblock annotations, which FOSRest no longer reads. The bundle's listeners observe the request/response cycle, so a controller that produces a well-formed View gets the extra behaviour for free. A controller that hand-rolls new JsonResponse(json_encode(...)) bypasses the serializer context entirely and the bundle will appear to do nothing.

Request-driven serialization context. The core workflow is letting the client narrow the payload:

#[Rest\Get('/api/pages/{id}')]
public function show(Page $page): View
{
    $view = $this->view($page, 200);
    $view->setContext($view->getContext() + [
        'groups' => ['page:read'],
    ]);
    return $view;
}

Treat the bundle's contribution as additive and untrusted: your controller's own groups define the maximum surface, and the request may only select a subset. Whitelist the group names the client is allowed to name (a const ALLOWED_GROUPS in a base controller or a config array) rather than passing the query string straight into the context.

A shared base controller pays off. Because the logic is per-response, put it in one place:

abstract class ApiController extends AbstractFOSRestController
{
    protected function apiView($data, int $status = 200, array $groups = []): View
    {
        $view = $this->view($data, $status);
        $view->setContext($view->getContext() + [
            'groups' => array_merge($this->defaultGroups(), $groups),
        ]);
        return $view;
    }
}

All your resources then inherit consistent behaviour, and if the bundle lags behind Symfony 7 the way it lagged behind 6, you have exactly one edit to make.

Integration with the format listener. Keep fos_rest.format_listener and view_response_listener enabled and let this bundle's subscribers run alongside them. Order matters: format negotiation should resolve _format before serialization context is computed, so if you see an Accept header being ignored, check debug:event-dispatcher kernel.request for who runs first and adjust priority in your own subscriber rather than patching vendor code. Re-check this explicitly after the v4 upgrade — a priority that worked on Symfony 4/5 is not guaranteed to be the effective order under 6.

Testing. Two levels, both cheap:

// functional — asserts the whole listener chain
$client = static::createClient();
$client->request('GET', '/api/pages/1?groups=page:read');
$client->assertResponseIsSuccessful();
$this->assertArrayNotHasKey('internalNotes', $client->getResponse()->toArray());
// unit — the listener in isolation, fast and stable across Symfony upgrades
$listener = new YourListener(/* ... */);
$listener->onKernelRequest($this->createRequestEvent('/api/pages?groups=a,b'));
$this->assertSame(['a', 'b'], $request->attributes->get('_groups'));

The unit test is what saves you when you upgrade Symfony and the functional test fails for unrelated reasons — exactly the situation v4 just created. Note that Symfony 6's KernelTestCase/WebTestCase API (assertResponseIsSuccessful, getResponse()->toArray()) differs from the 3.x/4.x idioms you'll find in older tutorials.

Compose with your serializer. If you use JMS, groups must exist in #[Groups] metadata or they are silently ignored. If you use the Symfony serializer, they must match #[Groups] attributes. A typo produces a full, unfiltered response with no error — add an assertion in a test that a nonexistent group still returns the safe default, so you notice when filtering breaks.

Gotchas and Tips

  • Still pin, but the story changed. v4.0.0 is a compatibility release with no functional changes, so the honest read is: the package is maintained on an as-needed basis, not abandoned. Pin ^4.0, commit composer.lock, and run composer why-not symfony/framework-bundle ^7.0 before any framework upgrade. If you're starting a new API today, still weigh a 20-line custom listener in your own codebase against the dependency — for a single-purpose bundle this small, inlining removes an upgrade liability for free, and v4 arriving years after Symfony 6 shipped is the evidence for why that matters.
  • Expect the next gap. Symfony 6 compatibility landed as a single-purpose release with a one-line changelog and no deprecation path from v1. Plan for the Symfony 7 equivalent to be a similar wait. If you need it sooner, fork and use a VCS repositories entry, or copy the listener into src/EventListener/ and drop the dependency — the code volume is small enough that a copy is a legitimate strategy.
  • v1 → v4 is a jump-skip. You cannot assume anything about intermediate 2.x/3.x behaviour; there is no migration guide. Treat it as "the same feature set, new framework constraints": diff the resolved container and the event dispatcher output before/after rather than auditing a changelog that doesn't exist. The most likely breakages are removed Symfony 3/4 APIs the bundle called, not changes in what the bundle does for you.
  • Listener priority is the number one debugging lever. Most "the bundle does nothing" reports are an ordering problem: FOSRest's body_listener, format_listener, or your own security subscriber runs before the bundle's subscriber and the request attributes it expects aren't there yet. Confirm with debug:event-dispatcher, then set an explicit priority on your overrides.
  • Never trust client-supplied groups/fields blindly. Exposing serialization groups from a query parameter is one typo away from leaking password hashes or internal flags. Maintain an allowlist and default to a safe group set when the parameter is absent or invalid.
  • Cache poisoning. If the response now varies by ?groups= or a custom header, your HTTP cache must know:
$response->setVary(['Accept', 'X-Serialization-Groups']);

Without it, a cached narrow response gets served to a client asking for the full one, or worse, a full response is served where filtering was expected. If you use Symfony HttpCache or Varnish, verify the Vary header in a test.

  • Error responses are a separate path. Exception listeners typically bypass the normal View flow, so your group filtering may not apply to 4xx/5xx payloads. Symfony 6 makes this more visible because the error renderer changed; assert on an error response in a test and, if needed, normalise it via a kernel.exception subscriber that produces a View like everything else.
  • Config quirks. Because there's no recipe, defaults are whatever Configuration.php says, not what the README implies. Dump the resolved tree with bin/console config:dump-reference m6web_fos_rest_extra (adjust the key using the bundle's DI alias) before assuming an option is on — and re-dump after the v4 upgrade, since a Configuration.php rewritten for Symfony 6's config component can change node defaults silently.
  • Extension points, in order of preference: (1) decorate the bundle's service via services.yaml decorates: to wrap behaviour without forking; (2) add your own subscriber at a chosen priority; (3) fork. Avoid editing vendor/ — even with an active v4 release, your patch will be lost on the next composer update, and a compat-only maintainer is unlikely to carry it upstream.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect