cesurapp/api-bundle

Symfony Api Bundle

安装: 200

依赖: 0

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:symfony-bundle

2.0.11 2024-09-19 21:28 UTC

README

App Tester Software License

此软件包允许您使用Symfony公开快速API端点。

功能

  • Json请求体转换器
  • 错误消息收集在单一格式下。
  • 对所有错误消息应用语言翻译。
  • 自定义CORS头部支持
  • 自动文档生成器(Thor)
  • TypeScript客户端生成器
  • Api DTO解析器
  • Doctrine筛选器 & 排序资源
  • PhoneNumber, UniqueEntity, Username验证器
  • Excel, Csv导出器(Sonata Export Bundle)

安装

需要Symfony 7

composer req cesurapp/api-bundle

配置: 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

生成TypeScript客户端

查看文档: http:://127.0.0.1:8000/thor

bin/console thor:extract ./path # Generate Documentation to Directory

创建API响应

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)
    }
}

创建API资源

筛选和DataTable仅在启用分页时工作。自动创建TS列用于表格。所有表格的导出都自动启用。

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
                ]
            ]
        ]   
    }

}

使用筛选

筛选器根据查询参数设置。仅过滤匹配的记录。

示例请求 http://example.test/v1/userlist?filter[id]=1&filter[createdAt][min]=10.10.2023

创建表单验证

后端日期以UTC ATOM格式存储。在GET请求中,您将收到ATOM格式的日期。在POST|PUT请求中,发送ATOM格式的日期,转换为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;
}