Я создал некоторый DTO, напрямую связанный с платформой API, например:
Код: Выделить всё
#[ApiResource]
#[Get(
uriTemplate: 'stock/components/{id}',
read: true,
provider: ComponentDtoProviderService::class,
)]
#[GetCollection(
uriTemplate: 'stock/components/',
provider: ComponentDtoProviderService::class
)]
#[Post(
uriTemplate: 'stock/components/',
validationContext: ['groups' => ['Default', 'postValidation']],
processor: ComponentDtoProcessorService::class,
)]
#[Delete(
uriTemplate: 'stock/components/{id}',
provider: ComponentDtoProviderService::class,
processor: ComponentDtoProcessorService::class
)]
#[Put(
uriTemplate: 'stock/components/',
validationContext: ['groups' => ['Default']],
processor: ComponentDtoProcessorService::class
)]
readonly class ComponentDto
{
public function __construct(
public ?string $id,
#[Assert\NotBlank(groups: ['postValidation'])]
public string $name,
public ?string $ean13,
public ?int $quantity,
public ?int $quantityThreshold,
) {
}
}
Я использую PHPunit для тестирования своего API, поэтому я создал тесты для операций get, getAll, post, delete и put/patch, используя ранее добавленные фикстуры (которые я получаю по имени).
Код: Выделить всё
public function testComponentsPut(): void
{
$componentBlueRose = $this->componentRepository->findByName(ComponentFixtures::COMPONENT_BLUE_ROSE);
self::assertNotNull($componentBlueRose);
// test new EAN13 and quantity and quantityThreshold
$componentBlueRoseDtoModified = new ComponentDto(
$componentBlueRose->getId()->toString(),
$componentBlueRose->getName(),
'9234569990123', // data are modified here
400, // data are modified here
8, // data are modified here
);
$response = parent::createClient()->request('PUT', 'api/stock/components/',
['json' => $componentBlueRoseDtoModified]);
self::assertResponseIsSuccessful();
$componentBlueRoseUpdated = $this->componentRepository->findByName(ComponentFixtures::COMPONENT_BLUE_ROSE);
// todo : its not working here
self::assertSame($componentBlueRoseDtoModified->ean13, $componentBlueRoseUpdated->getEan13());
self::assertSame($componentBlueRoseDtoModified->quantity, $componentBlueRoseUpdated->getQuantity());
self::assertSame($componentBlueRoseDtoModified->quantityThreshold, $componentBlueRoseUpdated->getQuantityThreshold());
}
Это заставило меня обнаружить странное поведение платформы API:
Когда вызываются операции PATCH или PUT, всегда вызывается поставщик, даже если он не указан, и он вызывается ДО того, как процессорная ведьма, на самом деле так и делает:
- DTO с новыми модификациями передается в HTTPRequest следующим образом:
Код: Выделить всё
$response = $client->request('PUT', 'api/stock/components/', ['json' => $componentBlueRoseDtoModified]); - Этот DTO перехватывается классом Provider, который принимает идентификатор, загружает новый объект из базы данных и отображает его как DTO, что правильное поведение операции GET. DTO, возвращаемый поставщиком, взят из базы данных, поэтому это не модифицированный DTO, указанный в HTTP-запросе.
- Этот DTO передается процессору, который сохраняет его.
Поскольку я знаю, что при указании /{id} для операций исправления и размещения требуется поставщик (в последних версиях платформы API), я попробовал оба пути:
- /api/comComponent/{id} с указанным поставщиком и процессором и
- /api/ компонент/ без поставщика
Он работает точно так же: тестирование напрямую с /api работает, но не в модульных тестах.
Код: Выделить всё
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|ComponentDto
{
// get all components
if ($operation instanceof CollectionOperationInterface) {
$components = $this->componentRepository->getAll();
$componentDtos = [];
foreach ($components as $component) {
$componentDtos[] = $this->getComponentDtoFromEntity($component);
}
return $componentDtos;
}
// get one component
if (!isset($uriVariables['id'])) {
throw new \Exception('Id is required');
}
$component = $this->componentRepository->findByIdAsString($uriVariables['id']);
if (null === $component) {
throw new \Exception('Component not found');
}
return $this->getComponentDtoFromEntity($component);
}
public function getComponentDtoFromEntity(Component $component): ComponentDto
{
return new ComponentDto(
$component->getId() === null ? null : (string) $component->getId(),
$component->getName(),
$component->getEan13(),
$component->getQuantity(),
$component->getQuantityThreshold(),
);
}
- Почему PATCH и PUT работают на самом деле, но не в модульных тестах? Как заставить это работать?
- Почему провайдер вызывается, даже если в атрибутах платформы API указан ТОЛЬКО процессор?
Подробнее здесь: https://stackoverflow.com/questions/788 ... works-with
Мобильная версия