Hi everyone!
I want to share a small but useful package I’ve made:
a7-tech/laminas-attribute-controller
This package lets you use PHP 8 attributes (like #[Route], #[Autowire], etc.) to define your Laminas controllers in a cleaner and more modern way — no need to write routes as arrays anymore, and possible to use like #MapRequestPayload to serialize and validate before action.
Key Features:
- #[Route] — define routes right on controllers or methods, auto-registered by the module
- #[QueryParam] — extract and validate GET query params via Symfony Validator (constraints, required flag)
- #[MapRequestPayload] — map POST/PUT/PATCH request body into a typed DTO and validate it automatically
- #[MapQueryString] — map GET query string into a typed DTO with validation rules
- #[Autowire] — inject services automatically by type (or alias) from the container
- #[IsGranted] — restrict access by roles (e.g. FULLY_AUTHENTICATED)
- #[CurrentUser] — inject the authenticated user object directly into your action method
Unlimited customization is also possible via custom ParameterResolverInterface implementations
use LaminasAttributeController\AttributeActionController;
use LaminasAttributeController\Routing\Route;
use LaminasAttributeController\Validation\MapRequestPayload;
use LaminasAttributeController\Validation\MapQueryString;
use LaminasAttributeController\Validation\QueryParam;
use LaminasAttributeController\Security\CurrentUser;
use LaminasAttributeController\Security\IsGranted;
#[Route('/api/users', name: 'users')]
final class UserController extends AttributeActionController
{
#[Route('/create', methods: ['POST'])]
public function create(
#[MapRequestPayload] UserCreateRequest $data
): array {
// $data DTO is populated and validated
return ['id' => $this->userService->create($data)];
}
#[Route('/search', methods: ['GET'])]
public function search(
#[MapQueryString] UserSearchRequest $criteria,
#[QueryParam('token', required: true)] string $token
): array {
// $criteria is filled and validated
return [
'criteria' => $criteria,
'token' => $token,
'results' => $this->userService->find($criteria)
];
}
#[Route('/profile', methods: ['GET'])]
#[IsGranted('FULLY_AUTHENTICATED')]
public function profile(#[CurrentUser] User $user): array {
return ['user' => $user];
}
}
Why use it:
If you like the clean, declarative style of frameworks like Symfony, but you use Laminas — this package can help improve your developer experience.
I’d love to hear your feedback!
I’m also planning to add more features like caching, OpenAPI support, and more.
Let me know what you think!