Weave Code
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

Rest Bundle Laravel Package

friendsofsymfony/rest-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require friendsofsymfony/rest-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        FriendsOfSymfony\RestBundle\FriendsOfSymfonyRestBundle::class => ['all' => true],
    ];
    
  2. Enable Format Negotiation: Configure config/packages/fos_rest.yaml:

    fos_rest:
        format_listener: true
        param_fetcher_listener: true
        body_listener: true
        view:
            view_response_listener: true
            formats:
                json: true
                xml: true
                rss: false
    
  3. First Use Case: Create a controller with a FOS\RestBundle\Controller\AbstractFOSRestController base class:

    use FOS\RestBundle\Controller\AbstractFOSRestController;
    use FOS\RestBundle\Controller\Annotations as Rest;
    use Symfony\Component\HttpFoundation\Response;
    
    class UserController extends AbstractFOSRestController
    {
        /**
         * @Rest\Get("/users/{id}", name="get_user")
         */
        public function getUserAction(int $id): Response
        {
            $user = $this->getDoctrine()->getRepository(User::class)->find($id);
            return $this->handleView($this->view($user, Response::HTTP_OK));
        }
    }
    

Implementation Patterns

Core Workflows

  1. Format-Agnostic Controllers: Use handleView() to let FOSRestBundle handle response formatting (JSON/XML) based on Accept header.

    return $this->handleView($this->view($data, Response::HTTP_CREATED));
    
  2. Request Body Parsing: Automatically deserialize request body into an object (e.g., DTO) using @Rest\RequestParam or @Rest\RequestBody.

    /**
     * @Rest\Post("/users")
     */
    public function createUserAction(UserDTO $userDTO): Response
    {
        // $userDTO is automatically populated from request body
    }
    
  3. Exception Handling: Map exceptions to HTTP status codes via fos_rest.exception.map in config:

    fos_rest:
        exception:
            map:
                App\Exception\NotFoundException: HTTP_NOT_FOUND
    
  4. Serialization Groups: Use Symfony Serializer groups to control which properties are serialized:

    use JMS\Serializer\Annotation as Serializer;
    
    class User
    {
        /**
         * @Serializer\Groups({"user:read"})
         */
        private $name;
    }
    

    Configure in fos_rest.yaml:

    fos_rest:
        view:
            serializer_groups: ['user:read']
    
  5. Custom Formats: Add support for custom formats (e.g., application/vnd.api+json):

    fos_rest:
        view:
            formats:
                jsonapi: true
    

    Register a custom formatter service:

    services:
        App\Serializer\JsonApiFormatter:
            tags: ['fos_rest.formatter']
    
  6. Pagination: Use Knp\Component\PagerFantaBundle with FOSRestBundle for paginated responses:

    use Knp\Component\PagerFanta\View\ViewInterface;
    
    public function listUsersAction(ViewInterface $pagerFantaAdapter): Response
    {
        return $this->handleView($this->view($pagerFantaAdapter->getCurrentPageResults()));
    }
    

Integration Tips

  1. API Platform Compatibility: FOSRestBundle works alongside API Platform. Use ApiPlatform\Core\Bridge\Symfony\FormatListener for unified format handling.

  2. Event Listeners: Attach listeners to fos_rest.exception or fos_rest.view events for custom logic:

    use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
    
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        if ($event->getThrowable() instanceof \RuntimeException) {
            $event->setResponse($this->createExceptionResponse($event->getThrowable()));
        }
    }
    
  3. Testing: Mock the Request object to test format negotiation:

    $client->request('GET', '/users', [], [], [
        'HTTP_ACCEPT' => 'application/json',
    ]);
    
  4. CORS: Combine with nelmio/cors-bundle for RESTful CORS support:

    nelmio_cors:
        defaults:
            allow_origin: ['*']
            allow_methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
            allow_headers: ['*']
            expose_headers: ['*']
    

Gotchas and Tips

Pitfalls

  1. Format Negotiation Overrides:

    • If format_listener is enabled, the Accept header takes precedence over route parameters (e.g., /users.{_format}).
    • Fix: Disable format_listener if you need strict route-based format control.
  2. Circular References:

    • Serialization errors may occur with circular references (e.g., User->orders->user).
    • Fix: Use @Serializer\MaxDepth or @Serializer\ExclusionPolicy(ALL) on problematic properties.
  3. Doctrine ORM vs. Serializer Conflicts:

    • Doctrine’s __toString() may interfere with serialization.
    • Fix: Exclude from serialization groups or override __toString():
      /**
       * @Serializer\ExclusionPolicy("all")
       */
      public function __toString() { return ''; }
      
  4. Body Listener Conflicts:

    • The body_listener may parse the request body before your controller logic runs, causing issues with raw input (e.g., file uploads).
    • Fix: Disable body_listener for specific routes or use @Rest\RequestParam for manual parsing.
  5. Deprecated Annotations:

    • Some annotations (e.g., @Rest\View) are deprecated in favor of handleView().
    • Fix: Update to modern patterns (e.g., return $this->view($data) + handleView()).

Debugging Tips

  1. Check Negotiated Format: Log the negotiated format in a controller:

    $format = $this->get('request')->getRequestFormat();
    $this->logger->debug('Negotiated format:', ['format' => $format]);
    
  2. Validate Serialization: Use the serializer.debug option to log serialization issues:

    fos_rest:
        view:
            serializer_debug: true
    
  3. Inspect Exceptions: Enable detailed exception rendering in dev environment:

    fos_rest:
        exception:
            enabled: true
    
  4. Common HTTP Status Codes:

    • Use Response::HTTP_CREATED (201) for successful POST/PUT.
    • Use Response::HTTP_NO_CONTENT (204) for DELETE operations with no response body.

Extension Points

  1. Custom Formatters: Create a formatter service to support non-standard formats:

    use FOS\RestBundle\Formatter\FormatterInterface;
    
    class CustomFormatter implements FormatterInterface
    {
        public function supports($format, $mediaType)
        {
            return 'custom' === $format;
        }
    
        public function format($data, $format, $context = [])
        {
            return json_encode($data, JSON_PRETTY_PRINT);
        }
    }
    

    Register as a service with the fos_rest.formatter tag.

  2. Override Default View: Extend the default FOS\RestBundle\View\View class to add custom logic:

    use FOS\RestBundle\View\View;
    
    class CustomView extends View
    {
        public function __construct($data, int $statusCode = 200, array $headers = [])
        {
            parent::__construct($data, $statusCode, $headers);
            $this->setHeader('X-Custom-Header', 'value');
        }
    }
    
  3. Dynamic Route Parameters: Use @Rest\QueryParam or @Rest\RequestParam for dynamic filtering:

    /**
     * @Rest\Get("/users", name="list_users")
     */
    public function listUsersAction(
        int $page = 1,
        int $limit = 10,
        ?string $filter = null
    ): Response {
        // Use $page, $limit, $filter
    }
    
  4. Event Subscribers: Subscribe to fos_rest.exception or fos_rest.view events for global modifications:

    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use FOS\RestBundle\Event\ExceptionEvent;
    
    class CustomSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                'fos_rest.exception' => 'onException',
            ];
        }
    
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware