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

Api Problem Bundle Laravel Package

1mcodes/api-problem-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require phpro/api-problem-bundle
    

    Add the bundle to config/bundles.php if not using Symfony Flex:

    Phpro\ApiProblemBundle\ApiProblemBundle::class => ['all' => true],
    
  2. First Use Case: Throw an ApiProblemException in a controller to return RFC7807-compliant error responses.

    use Phpro\ApiProblem\Exception\ApiProblemException;
    
    public function createAction() {
        throw new ApiProblemException(
            new \Phpro\ApiProblem\Problem(
                'https://example.com/probs/out-of-credit',
                'You do not have enough credit.',
                'https://example.com/docs/errors/out-of-credit'
            )
        );
    }
    
  3. Where to Look First:


Implementation Patterns

Core Workflow

  1. Error Handling: Replace generic exceptions with ApiProblemException for structured errors:

    throw new ApiProblemException(
        new \Phpro\ApiProblem\Problem(
            'invalid_request',
            'Invalid input data',
            'https://api.example.com/docs/errors/invalid_request',
            'https://example.com/probs/invalid-request',
            400,
            ['field' => 'email', 'reason' => 'invalid format']
        )
    );
    
  2. Controller Integration: Use _format="json" in routes to ensure JSON responses:

    #[Route('/api/users', name: 'create_user', methods: ['POST'], defaults: ['_format' => 'json'])]
    public function createUser(Request $request) {
        // ...
    }
    
  3. Validation Errors: Convert Symfony Validator errors to ApiProblem:

    use Symfony\Component\Validator\ConstraintViolationListInterface;
    
    public function validateAndThrow(ConstraintViolationListInterface $violations) {
        $problems = [];
        foreach ($violations as $violation) {
            $problems[] = new \Phpro\ApiProblem\Problem(
                'validation_error',
                $violation->getMessage(),
                null,
                null,
                400,
                ['property' => $violation->getPropertyPath()]
            );
        }
        throw new ApiProblemException($problems);
    }
    
  4. API Problem Types: Extend \Phpro\ApiProblem\Problem for custom error types:

    class PaymentFailedProblem extends \Phpro\ApiProblem\Problem {
        public function __construct(string $detail, array $extensions = []) {
            parent::__construct(
                'payment_failed',
                $detail,
                'https://api.example.com/docs/errors/payment_failed',
                null,
                402,
                $extensions
            );
        }
    }
    
  5. Global Exception Handling: Override the default listener in config/packages/phpro_api_problem.yaml:

    phpro_api_problem:
        exception_listener:
            enabled: true
            order: 100  # Priority
    

Gotchas and Tips

Pitfalls

  1. Format Mismatch:

    • If _format is missing or incorrect (e.g., _format="xml"), the bundle may not trigger.
    • Fix: Ensure routes use _format="json" or set Accept: application/problem+json.
  2. Exception Order:

    • The bundle’s ExceptionListener runs after Symfony’s default listener.
    • Fix: Adjust order in config if other listeners interfere.
  3. Nested Problems:

    • ApiProblemException accepts single or array of \Phpro\ApiProblem\Problem objects.
    • Gotcha: Passing a non-array when multiple problems exist will throw an error.
    • Fix: Always wrap multiple problems in an array:
      throw new ApiProblemException([$problem1, $problem2]);
      
  4. HTTP Status Codes:

    • The bundle ignores the status code in Problem if ApiProblemException uses a default HTTP code.
    • Fix: Use HttpApiProblem for explicit status codes:
      throw new ApiProblemException(
          new \Phpro\ApiProblem\HttpApiProblem(403, 'Forbidden')
      );
      

Debugging Tips

  1. Check Response Headers:

    • Valid RFC7807 responses include:
      Content-Type: application/problem+json
      
    • Use dd($exception) in a custom listener to inspect the Problem object.
  2. Log Unhandled Exceptions:

    • Wrap ApiProblemException in a try-catch to log details:
      try {
          // Risky operation
      } catch (ApiProblemException $e) {
          $this->logger->error('API Error', ['problem' => $e->getProblem()]);
          throw $e;
      }
      
  3. Custom Listener:

    • Override the default listener to add metadata:
      use Phpro\ApiProblemBundle\EventListener\ApiProblemExceptionListener;
      
      class CustomApiProblemListener extends ApiProblemExceptionListener {
          public function onKernelException(GetResponseForExceptionEvent $event) {
              $exception = $event->getThrowable();
              if ($exception instanceof ApiProblemException) {
                  $problem = $exception->getProblem();
                  $problem->with('debug', app()->environment() === 'dev');
                  $event->setResponse($this->createResponse($problem));
              }
          }
      }
      
    • Register in services.yaml:
      services:
          App\EventListener\CustomApiProblemListener:
              tags:
                  - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 100 }
      

Extension Points

  1. Problem Factories:

    • Create reusable factories for common errors:
      class ValidationProblemFactory {
          public static function createFromViolations(ConstraintViolationListInterface $violations): array {
              return array_map(function ($violation) {
                  return new \Phpro\ApiProblem\Problem(
                      'validation_error',
                      $violation->getMessage(),
                      null,
                      null,
                      400,
                      ['property' => $violation->getPropertyPath()]
                  );
              }, iterator_to_array($violations));
          }
      }
      
  2. Dynamic Problem Types:

    • Use traits to standardize error responses:
      trait ProblemTypes {
          protected function createNotFoundProblem(string $id): \Phpro\ApiProblem\Problem {
              return new \Phpro\ApiProblem\Problem(
                  'not_found',
                  sprintf('Resource %s not found', $id),
                  null,
                  null,
                  404
              );
          }
      }
      
  3. Testing:

    • Assert RFC7807 compliance in PHPUnit:
      $response = $this->client->get('/api/protected');
      $this->assertEquals(401, $response->getStatusCode());
      $this->assertEquals('application/problem+json', $response->headers->get('Content-Type'));
      $data = json_decode($response->getContent(), true);
      $this->assertArrayHasKey('type', $data);
      
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