Installation
composer require antonchernik/restful-bundle
Add to config/bundles.php:
RestfulBundle\RestfulBundle::class => ['all' => true],
Basic Configuration
In config/services.yaml:
parameters:
restful_bundle.validation.message_map: !php/const App\Dictionary\ValidationMessages::MESSAGE_MAP
(Use RestfulBundle\Dictionary\ValidationMessages::MESSAGE_MAP if no custom messages.)
First Use Case
Create a DTO (via antonchernik/dto-bundle) and annotate it with @Restful\ApiResource:
use RestfulBundle\Annotation\ApiResource;
#[ApiResource(
collectionOperations: ['get', 'post'],
itemOperations: ['get', 'put', 'delete']
)]
class UserDto {}
Register the DTO in your controller:
use RestfulBundle\Controller\AbstractRestfulController;
class UserController extends AbstractRestfulController
{
public function __construct(private UserDto $dto) {}
}
#[ApiResource(
collectionOperations: ['get', 'post' => ['method' => 'POST', 'validation_groups' => ['create']]],
itemOperations: ['get', 'put' => ['validation_groups' => ['update']]]
)]
class PostDto {}
message_map for localized errors.justinrainbow/json-schema.AbstractRestfulController to inherit CRUD logic:
class ProductController extends AbstractRestfulController
{
public function __construct(private ProductDto $dto) {}
// Override methods for custom logic (e.g., `createItemAction`).
}
collection(): Handles GET /collection, POST /collection.item(): Handles GET /item, PUT/PATCH /item, DELETE /item.publish):
#[ApiResource(
itemOperations: [
'publish' => [
'method' => 'POST',
'path' => '/{id}/publish',
'controller' => [UserController::class, 'publishAction']
]
]
)]
class UserDto {}
public function publishAction(UserDto $dto): Response
{
// Custom logic...
return $this->json($dto);
}
RestfulBundle\Transformer\DtoTransformer to customize serialization:
$transformer = new DtoTransformer($dto);
$data = $transformer->transform(['include' => ['author']]);
include=author,comments).antonchernik/logging-bundle for API request logging:
$this->logger->info('API Request', [
'dto' => $dto,
'method' => $request->getMethod(),
'path' => $request->getPathInfo()
]);
DTO Validation Groups
validation_groups in @ApiResource causes all constraints to run on every operation.post: ['validation_groups' => ['create']]
put: ['validation_groups' => ['update']]
Circular References in DTOs
User ↔ Post) may cause serialization errors.@Restful\Ignore on problematic properties or configure DtoTransformer to handle cycles.Overriding Default Paths
@ApiResource must include {id} for item operations or the bundle’s router won’t bind the DTO.{id} in paths like /items/{id}.Symfony 6+ Sensio Extensions
sensio/framework-extra-bundle v6+. Older versions may break route binding.^6.0 and clear cache:
composer require sensio/framework-extra-bundle:^6.0
php bin/console cache:clear
Validation Errors
message_map parameter in services.yaml for custom error messages.# config/packages/validator.yaml
validator:
enable_annotation_reader: true
DTO Binding Issues
dd($this->getDto()) in your controller to inspect bound data.Serialization Quirks
DateTime without a getter).JsonSerializable implementations conflicting with the transformer.Custom Transformers
DtoTransformer to add logic:
class CustomTransformer extends DtoTransformer
{
protected function transformProperty($property, $value): mixed
{
if ($property === 'price') {
return number_format($value, 2);
}
return parent::transformProperty($property, $value);
}
}
services.yaml:
services:
App\Transformer\CustomTransformer: ~
Event Listeners
restful.pre_validate):
use RestfulBundle\Event\PreValidateEvent;
$eventDispatcher->addListener(PreValidateEvent::NAME, function (PreValidateEvent $event) {
if ($event->getDto() instanceof UserDto) {
$event->setValidationGroups(['create', 'custom']);
}
});
Dynamic DTOs
antonchernik/dto-bundle’s DynamicDto for runtime-generated schemas:
$dynamicDto = new DynamicDto([
'name' => ['type' => 'string', 'constraints' => [new NotBlank()]],
'tags' => ['type' => 'array', 'items' => ['type' => 'string']]
]);
API Versioning
api_platform or nelmio/api-doc-bundle for versioned endpoints:
# config/routes.yaml
app_api_v1:
resource: "@RestfulBundle/Resources/config/routing/v1.yaml"
prefix: /api/v1
How can I help you explore Laravel packages today?