alexandrebulete/ddd-apiplatform-bundle
Install the Bundle
composer require alexandrebulete/ddd-apiplatform-bundle
Ensure your config/bundles.php includes:
AlexandreBulete\DddApiPlatformBundle\DddApiPlatformBundle::class => ['all' => true],
Register the Bridge
The bundle auto-wires ddd-apiplatform-bridge services, including the Paginator for state providers.
First Use Case: Paginated Query via State Provider
Create a state provider for a DDD query (e.g., GetPostsQuery) and return a Paginator:
use AlexandreBulete\DddApiPlatformBridge\State\Paginator;
use ApiPlatform\State\ProviderInterface;
class GetPostsProvider implements ProviderInterface {
public function provide(Operation $operation, array $uriVariables, array $context) {
$posts = $this->queryBus->ask(new GetPostsQuery());
return new Paginator($posts, $operation->getPaginationContext());
}
}
Tag it in your API resource:
# config/api_platform/resources.yaml
App\Entity\Post:
collectionOperations:
get:
method: 'GET'
state: App\State\GetPostsProvider
Operation to access pagination context (e.g., ?page=2&limit=10).QueryBus) and wrap results in Paginator.X-Total-Count, Link).Example with filtering:
public function provide(Operation $operation, array $uriVariables) {
$filters = $operation->getPaginationContext()['filters'] ?? [];
$query = new GetFilteredPostsQuery($filters);
$posts = $this->queryBus->ask($query);
return new Paginator($posts, $operation->getPaginationContext());
}
GetPostsQuery) in your domain.Paginator to standardize pagination across endpoints.Example project structure:
/src
/Domain/Post/Query/GetPostsQuery.php
/Application/State/GetPostsProvider.php
/Infrastructure/ApiPlatform/Resources/Post.yaml
Extend Paginator for custom logic (e.g., nested pagination):
class NestedPaginator extends Paginator {
public function __construct(array $items, array $context, private int $maxDepth = 2) {
parent::__construct($items, $context);
}
// Custom logic...
}
$operation->getPaginationContext() to dynamically adjust queries.#[ApiResource]).Pagination Context Mismatch
Paginator expects page/limit in $context. If missing, API Platform throws errors.$context = $operation->getPaginationContext();
if (empty($context['page'])) $context['page'] = 1;
QueryBus Dependency
QueryBusInterface causes runtime errors.public function __construct(private QueryBusInterface $queryBus) {}
Entity Serialization
#[ApiResource] or serialization groups.#[Groups] for partial serialization.\Log::debug('Pagination Context:', $operation->getPaginationContext());
$items is an array/iterable and $context contains page/limit.Custom Paginator Classes
Override Paginator to add features (e.g., caching, analytics):
class AnalyticsPaginator extends Paginator {
public function __construct(array $items, array $context) {
parent::__construct($items, $context);
$this->trackUsage(); // Custom logic
}
}
Dynamic State Providers Use Symfony’s compiler pass to auto-register providers based on DDD queries:
// src/Kernel.php
$container->registerForAutoconfiguration(GetPostsProvider::class)
->addTag('api_platform.state_provider');
Testing
Mock QueryBus and Operation in unit tests:
$operation = $this->createMock(Operation::class);
$operation->method('getPaginationContext')->willReturn(['page' => 1, 'limit' => 10]);
DddApiPlatformBundle loads after ApiPlatformBundle in bundles.php.#[ReturnTypeWillChange] if migrating from older PHP.
```markdown
## Performance Considerations
- **Pagination Early:** Apply filtering/sorting in the DDD query (e.g., `GetFilteredPostsQuery`) to reduce database load.
- **Lazy Loading:** Use `Paginator` with lazy-loaded collections (e.g., Doctrine `Criteria`) for large datasets.
How can I help you explore Laravel packages today?