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

Symfony Jsonapi Bundle Laravel Package

alexfigures/symfony-jsonapi-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require alexfigures/symfony-jsonapi-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        AlexFigures\JsonApiBundle\JsonApiBundle::class => ['all' => true],
    ];
    
  2. Basic Controller:

    use AlexFigures\JsonApiBundle\Controller\JsonApiController;
    use Symfony\Component\HttpFoundation\Response;
    
    class ArticleController extends JsonApiController
    {
        public function index(): Response
        {
            return $this->collection('articles', ArticleResource::class);
        }
    }
    
  3. Resource Class:

    use AlexFigures\JsonApiBundle\Resource\ResourceInterface;
    
    class ArticleResource implements ResourceInterface
    {
        public function getType(): string { return 'articles'; }
        public function getId(): string { return $this->article->id; }
        public function getAttributes(): array { return $this->article->toArray(); }
    }
    
  4. Routing:

    # config/routes.yaml
    api_articles:
        path: /api/articles
        controller: App\Controller\ArticleController::index
        methods: [GET]
    

First Use Case

Create a simple GET endpoint returning paginated JSON:API-compliant data:

// src/Controller/ArticleController.php
public function index(): Response
{
    $articles = ArticleRepository::findAllWithPagination();
    return $this->collection('articles', ArticleResource::class, $articles);
}

Implementation Patterns

Core Workflows

  1. Resource-Based Routing:

    // Automatically maps to /api/articles/{id}
    public function show(string $id): Response
    {
        return $this->item('articles', ArticleResource::class, $id);
    }
    
  2. Sparse Fieldsets:

    // Only returns requested fields
    public function index(): Response
    {
        return $this->collection(
            'articles',
            ArticleResource::class,
            ArticleRepository::findAll(),
            ['fields[articles]' => 'title,body']
        );
    }
    
  3. Relationship Handling:

    // To-many relationship
    public function comments(string $id): Response
    {
        return $this->relationship(
            'articles',
            'comments',
            ArticleResource::class,
            $id,
            CommentResource::class,
            ArticleRepository::find($id)->comments
        );
    }
    

Integration Patterns

  1. Doctrine Integration:

    use AlexFigures\JsonApiBundle\Resource\Doctrine\DoctrineResource;
    
    class ArticleResource extends DoctrineResource
    {
        protected static string $entityClass = Article::class;
        protected static array $fields = ['title', 'body', 'publishedAt'];
    }
    
  2. Custom Error Handling:

    use AlexFigures\JsonApiBundle\Exception\JsonApiHttpException;
    
    try {
        return $this->item(...);
    } catch (EntityNotFoundException $e) {
        throw new JsonApiHttpException(404, 'Article not found', [
            'source' => ['pointer' => '/data/attributes/id']
        ]);
    }
    
  3. Event Listeners:

    use AlexFigures\JsonApiBundle\Event\ResourceEvent;
    
    public function onResourceBuild(ResourceEvent $event): void
    {
        if ($event->getResource() instanceof ArticleResource) {
            $event->getData()->setAttribute('author', $event->getResource()->getAuthor());
        }
    }
    

Performance Patterns

  1. Pagination:

    // Page-based pagination
    return $this->collection(
        'articles',
        ArticleResource::class,
        ArticleRepository::findAllPaginated(),
        [],
        ['page[size]' => 20, 'page[number]' => 1]
    );
    
  2. Caching:

    # config/packages/cache.yaml
    framework:
        cache:
            app.jsonapi: ~
    
    use Symfony\Component\HttpFoundation\Response;
    
    public function index(): Response
    {
        return $this->collection(
            'articles',
            ArticleResource::class,
            ArticleRepository::findAll(),
            [],
            [],
            ['cache' => ['ttl' => 300]]
        );
    }
    

Gotchas and Tips

Common Pitfalls

  1. Field Name Validation:

    • Reserved words (id, type) cannot be used as attributes unless explicitly allowed
    • Field names with special characters (@, spaces) will trigger 400 errors
    • Fix: Validate field names early in your resource class:
      public function getAttributes(): array
      {
          $attributes = $this->article->toArray();
          return array_filter($attributes, fn($k) => !in_array($k, ['id', 'type']), ARRAY_FILTER_USE_KEY);
      }
      
  2. Relationship Data Structure:

    • Always include data array even for empty relationships:
      return [
          'data' => [] // Not null or omitted
      ];
      
  3. Pagination Links:

    • Missing links in paginated responses will fail conformance
    • Fix: Use the built-in pagination helpers:
      return $this->collection(
          'articles',
          ArticleResource::class,
          $articles,
          [],
          [],
          ['pagination' => true]
      );
      

Debugging Tips

  1. Enable Debug Mode:

    # config/packages/dev/jsonapi.yaml
    jsonapi:
        debug: true
    
    • Shows detailed error messages and request/response dumps
  2. Validation Errors:

    • Check for errors array in responses (400 status)
    • Use jsonapi:validate command:
      php bin/console jsonapi:validate path/to/your/resource.json
      
  3. Performance Profiling:

    php bin/console debug:autowiring AlexFigures\JsonApiBundle
    
    • Identify slow service calls in the bundle

Extension Points

  1. Custom Resource Builders:

    use AlexFigures\JsonApiBundle\Resource\ResourceBuilderInterface;
    
    class CustomResourceBuilder implements ResourceBuilderInterface
    {
        public function build(array $data, string $type, string $id): ResourceInterface
        {
            return new CustomResource($data, $type, $id);
        }
    }
    

    Register in config:

    jsonapi:
        resource_builder: App\Service\CustomResourceBuilder
    
  2. Hook System:

    use AlexFigures\JsonApiBundle\Event\ResourceEvent;
    
    // Subscribe to resource building
    $dispatcher->addListener(ResourceEvent::RESOURCE_BUILD, [$this, 'onResourceBuild']);
    
    // Subscribe to response building
    $dispatcher->addListener(ResourceEvent::RESPONSE_BUILD, [$this, 'onResponseBuild']);
    
  3. Custom Error Providers:

    use AlexFigures\JsonApiBundle\Error\ErrorProviderInterface;
    
    class CustomErrorProvider implements ErrorProviderInterface
    {
        public function getError(int $status, string $title, array $meta = []): array
        {
            return [
                'errors' => [
                    [
                        'status' => $status,
                        'title' => $title,
                        'meta' => [
                            'custom' => 'value',
                            ...$meta
                        ]
                    ]
                ]
            ];
        }
    }
    

    Configure in services:

    services:
        App\Error\CustomErrorProvider:
            tags: ['jsonapi.error_provider']
    

Configuration Quirks

  1. Strict Mode:

    jsonapi:
        strict: true # Enforces all MUST requirements
    
    • Disables SHOULD/MAY behaviors for stricter compliance
  2. Profile Support:

    jsonapi:
        profiles:
            - 'https://example.com/profiles/article.v1'
            - 'https://jsonapi.org/format'
    
  3. Surrogate Keys:

    jsonapi:
        surrogate_keys:
            articles: 'articles/{id}'
    

Testing Tips

  1. Test Helpers:

    use AlexFigures\JsonApiBundle\Test\JsonApiTestCase;
    
    class ArticleTest extends JsonApiTestCase
    {
        public function testArticleResource()
        {
            $response = $this->client->request('GET', '/api/articles/1');
            $this->assertJsonApiResponse($response, 200);
            $this->assertJsonApiDocument($response, [
                'data' => [
                    'type' => 'articles',
                    'id' => '1',
                    'attributes' => [
                        'title' => 'Test Article',
                        'body' => 'Content...'
                    ]
                ]
            ]);
        }
    }
    
  2. Snapshot Testing:

    use AlexFigures\JsonApiBundle\Test\SnapshotTestTrait;
    
    class ConformanceTest extends TestCase
    {
    
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle