Бандл расширяет компонет Symfony http-foundation позволя выделить работу с API в отдельную инкапсулированную зону.
Предоставляется работа с контентом запроса в формате JSON посредством ParameterBag.
Архитектура бандла не допускает фатального падения в зоне API и всегда возвращает валидный ответ
с соответствующем кодом ошибки. Полный список кодов ошибок доступен в виде констант в классе
ApiException.
Для описания спецификации API обязательно использование Swagger 2 в одном из форматов:
- NelmioApiDocBundle.
- swagger-php.
- Использование файла конфигурации в формате
jsonилиyaml(yml).
Откройте консоль и, перейдя в директорию проекта, выполните следующую команду для загрузки наиболее подходящей стабильной версии этого бандла:
composer require wakeapp/api-platform-bundleЭта команда подразумевает что Composer установлен и доступен глобально.
После включите бандл добавив его в список зарегистрированных бандлов в app/AppKernel.php файл вашего проекта:
<?phpdeclare(strict_types=1);
// app/AppKernel.phpclass AppKernel extends Kernel
{
// ...publicfunctionregisterBundles()
{
$bundles = [
// ...newLinkin\Bundle\SwaggerResolverBundle\LinkinSwaggerResolverBundle(),
newWakeapp\Bundle\ApiPlatformBundle\WakeappApiPlatformBundle(),
];
return$bundles;
}
// ...
}Чтобы начать использовать бандл требуется определить создать и определить guesser - объект содержащий правила
определения зоны API вашего проекта.
<?phpdeclare(strict_types=1);
namespaceApp\Guesser\ApiAreaGuesser;
useSymfony\Component\HttpFoundation\Request;
useWakeapp\Bundle\ApiPlatformBundle\Guesser\ApiAreaGuesserInterface;
class ApiAreaGuesser implements ApiAreaGuesserInterface
{
/** * {@inheritDoc} */publicfunctiongetApiVersion(Request$request): ?int
{
$apiVersionMatch = [];
preg_match('/^\/v([\d]+)\//i', $request->getPathInfo(), $apiVersionMatch);
if (empty($apiVersionMatch)) {
returnnull;
}
$apiVersion = (int) end($apiVersionMatch);
return$apiVersion;
}
/** * {@inheritdoc} */publicfunctionisApiRequest(Request$request): bool
{
returnstrpos($request->getPathInfo(), '/api') === 0;
}
}Примечание: Если вы не используете autowire то вам необходимо зарегистрировать ApiAreaGuesser как сервис.
# app/config.ymlwakeapp_api_platform:
api_area_guesser_service: App\Guesser\ApiAreaGuesser# app/config.ymlwakeapp_api_platform:
# полное имя класса DTO для стандартизации ответаapi_result_dto_class: Wakeapp\Bundle\ApiPlatformBundle\Dto\ApiResultDto# идентификатор сервиса для определения зоны APIapi_area_guesser_service: App\Guesser\ApiAreaGuesser# идентификатор сервиса для глобального отлавливания ошибок и выдачи специализированных сообщений вместо 500error_code_guesser_service: Wakeapp\Bundle\ApiPlatformBundle\Guesser\ApiErrorCodeGuesser# Минимально допустимая версия API.minimal_api_version: 1# флаг для отладки ошибок - если установлен в true - ответ ошибки содержит trace.response_debug: falseИспользование функционала бандла начинается с создание контроллера и первого метода в указанной зоне API. В качестве примера рассмотрим возвращение простейшего профиля пользователя.
Для начала нам необходимо создать DTO возвращаемых данных:
<?phpdeclare(strict_types=1);
namespaceApp\Dto;
useSwagger\AnnotationsasSWG;
useWakeapp\Component\DtoResolver\Dto\DtoResolverTrait;
useWakeapp\Component\DtoResolver\Dto\DtoResolverInterface;
/** * @SWG\Definition( * type="object", * description="Profile info", * required={"email", "firstName", "lastName"}, * ) */class ProfileResultDto implements DtoResolverInterface
{
use DtoResolverTrait;
/** * @var string * * @SWG\Property(description="Profile email", example="test@gmail.com") */protected$email;
/** * @var string * * @SWG\Property(description="User's first name", example="John") */protected$firstName;
/** * @var string * * @SWG\Property(description="User's last name", example="Doe") */protected$lastName;
/** * @return string */publicfunctiongetEmail(): string
{
return$this->email;
}
/** * @return string */publicfunctiongetFirstName(): string
{
return$this->firstName;
}
/** * @return string */publicfunctiongetLastName(): string
{
return$this->lastName;
}
}Теперь добавим контроллер с соответствующим методом. Примечание: в качестве примера реализации используется подключение NelmioApiDocBundle.
<?phpdeclare(strict_types=1);
namespaceApp\Controller;
useApp\Dto\ProfileResultDto;
useNelmio\ApiDocBundle\Annotation\Model;
useSwagger\AnnotationsasSWG;
useSymfony\Component\Routing\Annotation\Route;
useWakeapp\Bundle\ApiPlatformBundle\Factory\ApiDtoFactory;
useWakeapp\Bundle\ApiPlatformBundle\HttpFoundation\ApiResponse;
/** * @Route("/api/profile") */class ProfileController
{
/** * Returns user profile * * @Route(methods={"GET"}) * * @SWG\Response( * response=ApiResponse::HTTP_OK, * description="Successful result in 'data' offset", * @Model(type=ProfileResultDto::class) * ) * * @param ApiDtoFactory $factory * * @return ApiResponse */publicfunctiongetProfile(ApiDtoFactory$factory): ApiResponse
{
// обработка данных$resultDto = $factory->createApiDto(ProfileResultDto::class, [
'email' => 'test-user@mail.ru',
'firstName' => 'Test',
'lastName' => 'User',
]);
returnnewApiResponse($resultDto);
}
}Объект на DTO:
<?phpdeclare(strict_types=1);
namespaceApp\Dto;
useSwagger\AnnotationsasSWG;
useWakeapp\Bundle\ApiPlatformBundle\Dto\MagicAwareDtoResolverTrait;
useWakeapp\Component\DtoResolver\Dto\DtoResolverInterface;
/** * @SWG\Definition( * type="object", * description="Update profile info", * required={"firstName", "lastName"}, * ) * * @method getEmail(): string */class UpdateProfileEntryDto implements DtoResolverInterface
{
use MagicAwareDtoResolverTrait;
/** * @var string */protected$email;
/** * @var string * * @SWG\Property(description="User's first name", example="John") */protected$firstName;
/** * @var string * * @SWG\Property(description="User's last name", example="Doe") */protected$lastName;
/** * @return string */publicfunctiongetFirstName(): string
{
return$this->firstName;
}
/** * @return string */publicfunctiongetLastName(): string
{
return$this->lastName;
}
}Контроллер:
<?phpdeclare(strict_types=1);
namespaceApp\Controller;
useApp\Dto\UpdateProfileEntryDto;
useNelmio\ApiDocBundle\Annotation\Model;
useSwagger\AnnotationsasSWG;
useSymfony\Component\Routing\Annotation\Route;
useWakeapp\Bundle\ApiPlatformBundle\HttpFoundation\ApiResponse;
/** * @Route("/api/profile") */class ProfileController
{
/** * Update user password * * @Route("/{email}", methods={"PATCH"}) * * @SWG\Parameter(name="email", in="path", type="string", required=true, description="User email") * @SWG\Parameter(name="body", in="body", @Model(type=UpdateProfileEntryDto::class), required=true) * * @param UpdateProfileEntryDto $entryDto * * @return ApiResponse */publicfunctiongetProfile(UpdateProfileEntryDto$entryDto): ApiResponse
{
returnnewApiResponse($entryDto);
}
}Чтобы описать формат обертки ответа Wakeapp\Bundle\ApiPlatformBundle\Dto\ApiResultDto при помощи аннотаций
можно использовать следующий подход:
Шаг 1 Создайте собственный ответ сервера:
<?phpdeclare(strict_types=1);
namespaceApp\Dto;
useSwagger\AnnotationsasSWG;
useWakeapp\Bundle\ApiPlatformBundle\Dto\ApiResultDtoasBaseApiResultDto;
useWakeapp\Component\DtoResolver\Dto\DtoResolverInterface;
/** * @SWG\Definition( * type="object", * description="Common API response object template", * required={"code", "message"}, * ) */class ApiResultDto extends BaseApiResultDto
{
/** * @var int * * @SWG\Property(description="Response api code", example=0, default=0) */protected$code = 0;
/** * @var string * * @SWG\Property(description="Localized human readable text", example="Successfully") */protected$message;
/** * @var DtoResolverInterface|null * * @SWG\Property(type="object", description="Some specific response data or null") */protected$data = null;
}Шаг 2 Добавьте в конфигурацию:
# app/config.ymlwakeapp_api_platform:
api_result_dto_class: App\Dto\MyApiResultDtoШаг 3 Добавьте описание к вашему методу:
<?phpdeclare(strict_types=1);
useNelmio\ApiDocBundle\Annotation\Model;
useSwagger\AnnotationsasSWG;
useWakeapp\Bundle\ApiPlatformBundle\HttpFoundation\ApiRequest;
useWakeapp\Bundle\ApiPlatformBundle\HttpFoundation\ApiResponse;
useWakeapp\Bundle\ApiPlatformBundle\Factory\ApiDtoFactory;
class ProfileController
{
/** * ... * * @SWG\Parameter(name="username", in="query", type="string", required=true, description="User login") * @SWG\Response( * response=ApiResponse::HTTP_OK, * description="Successful result in 'data' offset", * @Model(type=ProfileResultDto::class) * ) * @SWG\Response(response="default", @Model(type=ApiResultDto::class), description="Response wrapper") * * @param ApiRequest $apiRequest * @param ApiDtoFactory $factory * * @return ApiResponse */publicfunctiongetProfile(ApiRequest$apiRequest, ApiDtoFactory$factory): ApiResponse
{
returnnewApiResponse();
}
}