cesurapp / api-bundle
Symfony Api Bundle
Installs: 228
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: >=8.2
- doctrine/doctrine-bundle: ^2.10
- doctrine/orm: ^2.15
- giggsey/libphonenumber-for-php-lite: ^8.13
- sonata-project/exporter: ^3.3
- symfony/dependency-injection: ^7.0
- symfony/filesystem: ^7.0
- symfony/framework-bundle: ^7.0
- symfony/http-kernel: ^7.0
- symfony/intl: ^7.0
- symfony/mime: ^7.0
- symfony/security-bundle: ^7.0
- symfony/translation: ^7.0
- symfony/validator: ^7.0
Requires (Dev)
- php-cs-fixer/shim: ^3.40
- phpstan/phpstan: ^1.10
- symfony/test-pack: ^1.1
README
This package allows you to expose fast api endpoints with Symfony.
Features:
- Json request body transformer
- Error messages are collected under a single format.
- Language translation is applied to all error messages.
- Custom cors header support
- Automatic documentation generator (Thor)
- Typescript client generator
- Api DTO resolver
- Doctrine filter & sorter resource
- PhoneNumber, UniqueEntity, Username validator
- Excel, Csv exporter (Sonata Export Bundle)
Install
Required Symfony 7
composer req cesurapp/api-bundle
Configuration: config/packages/api.yaml
api: exception_converter: false cors_header: - { name: 'Access-Control-Allow-Origin', value: '*' } - { name: 'Access-Control-Allow-Methods', value: 'GET,POST,PUT,PATCH,DELETE' } - { name: 'Access-Control-Allow-Headers', value: '*' } - { name: 'Access-Control-Expose-Headers', value: 'Content-Disposition' } thor: base_url: "%env(APP_DEFAULT_URI)%" global_config: authHeader: Content-Type: application/authheader Authorization: 'Bearer Token' query: [] request: [] header: Content-Type: application/header Accept: application/headaadsa response: [] isAuth: true isPaginate: true isHidden: false
Generate TypeScript Client
View Documentation: http:://127.0.0.1:8000/thor
bin/console thor:extract ./path # Generate Documentation to Directory
Create Api Response
use \Cesurapp\ApiBundle\AbstractClass\ApiController; use \Cesurapp\ApiBundle\Response\ApiResponse; use \Cesurapp\ApiBundle\Thor\Attribute\Thor; use \Symfony\Component\Routing\Annotation\Route; class TestController extends ApiController { #[Thor( stack: 'Login|1', title: 'Login EndPoint', info: "Description", request: [ 'username' => 'string', 'password' => 'string', ], response: [ 200 => ['data' => UserResource::class], BadCredentialsException::class, TokenExpiredException::class, AccessDeniedException::class ], dto: LoginDto::class, isAuth: false, isPaginate: false, order: 0 )] #[Route(name: 'Login', path: '/login', methods: ['POST'])] public function getMethod(LoginDto $loginDto): ApiResponse { return ApiResponse::create() ->setData(['custom-data']) ->setQuery('QueryBuilder') ->setHTTPCache(60) // Enable HTTP Cache ->setPaginate() // Enable QueryBuilder Paginator ->setHeaders([]) // Custom Header ->setResource(UserResource::class) } #[Thor( stack: 'Profile|2', title: 'Profile EndPoint', query: [ 'name' => '?string', 'filter' => [ 'id' => '?int', 'name' => '?string', 'fullName' => '?string', ], ], response: [200 => ['data' => UserResource::class]], isAuth: true, isPaginate: false, order: 0 )] #[Route(name: 'GetExample', path: '/get', methods: ['GET'])] public function postMethod(): ApiResponse { $query = $userRepo->createQueryBuilder('q'); return ApiResponse::create() ->setQuery($query) ->setPaginate() // Enable QueryBuilder Paginator ->setHeaders([]) // Custom Header ->setResource(UserResource::class) } }
Create Api Resource
Filter and DataTable only work when pagination is enabled. Automatic TS columns are created for the table. Export is automatically enabled for all tables.
use \Cesurapp\ApiBundle\Response\ApiResourceInterface; class UserResource implements ApiResourceInterface { public function toArray(mixed $item, mixed $optional = null): array { return [ 'id' => $object->getId(), 'name' => $object->getName() ] } public function toResource(): array { return [ 'id' => [ 'type' => 'string', // Typescript Type -> ?string|?int|?boolean|?array|?object|NotificationResource::class| 'filter' => static function (QueryBuilder $builder, string $alias, mixed $data) {}, // app.test?filter[id]=test 'table' => [ // Typescript DataTable Types 'label' => 'ID', // DataTable Label 'sortable' => true, // DataTable Sortable Column 'sortable_default' => true, // DataTable Default Sortable Column 'sortable_desc' => true, // DataTable Sortable DESC 'filter_input' => 'input', // DataTable Add Filter Input Type -> input|number|date|daterange|checkbox|country|language // These fields are used in the backend. It doesn't transfer to the frontend. 'exporter' => static fn($v) => $v, // Export Column Template 'sortable_field' => 'firstName', // Doctrine Getter Method 'sortable_field' => static fn (QueryBuilder $builder, string $direction) => $builder->orderBy('u.firstName', $direction), ], ], 'created_at' => [ 'type' => 'string', 'filter' => [ 'from' => static function (QueryBuilder $builder, string $alias, mixed $data) {}, // app.test?filter[created_at][min]=test 'to' => static function (QueryBuilder $builder, string $alias, mixed $data) {}, // app.test?filter[created_at][max]=test ] ] ] } }
Using Filter
Filters are set according to the query parameter. Only matching records are filtered.
Sample request http://example.test/v1/userlist?filter[id]=1&filter[createdAt][min]=10.10.2023
Create Form Validation
Backend dates are stored in UTC ATOM format. In GET requests you get dates in ATOM format. In POST|PUT requests, send dates in ATOM format, converted to UTC.
use Cesurapp\ApiBundle\AbstractClass\ApiDto; use Cesurapp\ApiBundle\Thor\Attribute\ThorResource; use Symfony\Component\Validator\Constraints as Assert; class LoginDto extends ApiDto { /** * Enable Auto Validation -> Default Enabled */ protected bool $auto = true; /** * Form Fields */ #[Assert\NotNull] public string|int|null|bool $name; #[Assert\Length(min: 3, max: 100)] public ?string $lastName; #[Assert\Length(min: 10, max: 100)] #[Assert\NotNull] public int $phone; #[Assert\NotNull] #[Assert\GreaterThan(new \DateTimeImmutable())] public \DateTimeImmutable $send_at; #[Assert\Optional([ new Assert\Type('array'), new Assert\Count(['min' => 1]), new Assert\All([ new Assert\Collection([ 'slug' => [ new Assert\NotBlank(), new Assert\Type(['type' => 'string']), ], 'label' => [ new Assert\NotBlank(), ], ]), ]), ])] #[ThorResource(data: [[ 'slug' => 'string', 'label' => 'string|int|boolean', ]])] public ?array $data; }