上下文
我正在尝试在API平台中实现一个自定义端点,以从CSV创建一个资源集合。
我制作了一个DTO类EntitiesMultipleRecords
来存储CSV记录。每个记录都是一个实体的属性的关联数组。自定义规范化器EntitiesMultipleRecordsCSVDenormalizer
是我的用例所必需的。设置它是为了更改内置SymfonyCsvEncoder
的输出,并将其反规范化为我的DTO类。DataTransformerMultipleEntitiesCSVDataTransformer
处理到实体数组的转换。DataTransformer和自定义反序列化程序都注册为服务,反规范化程序服务标记为symfony.normalizer
。
作为验证整个过程的快速测试,我把正确处理它的例程放在一起:
$decoder = new Serializer(
[new EntitiesMultipleRecordsCSVDenormalizer()],
[new CsvEncoder([CsvEncoder::DELIMITER_KEY => ';'])]
);
$content = file_get_contents(
"{$kernel->getProjectDir()}/tests/_data/imports/dna/dna_import.csv"
);
$results = $decoder->deserialize($content, EntitiesMultipleRecords::class, 'csv');
$trans = new MultipleEntitiesCSVDataTransformer($em);
$entities = $trans->transform($results, Dna::class);
// got an array of DNA entities
// do some persisting and return response
问题是:
因此,所有单独的部分都可以一起工作,但我无法找到配置API平台以按预期方式使用它们的方法。
我发现了这个例子,并尝试复制类似的配置(下面的代码示例)。
然而,在使用swagger UI测试端点时,我得到了以下错误:
App\Controller\API\ImportCSVAction::__invoke():
Argument #1 ($data) must be of type App\DTO\EntitiesMultipleRecords, App\Entity\Dna given
我显然错过了什么。将__invoke
方法参数的类型提示更改为Dna
不会修复任何问题,实体为空。此外,如果可以直接使用反序列化实体,则需要Dna
数组,而不是单个数组。
我想甚至没有使用反序列化管道,尽管我设置了资源以使用我的DTO作为输入类。
资源配置:
* @ApiResource(
* collectionOperations={
* "import": {
* "method": "POST",
* "path": "/dnas/import",
* "input": EntitiesMultipleRecords::class,
* "controller": ImportCSVAction::class,
* "input_formats": {"csv": {"text/csv"}},
* }
* },
* )
class Dna {
//...
}
这是动作类:
namespace AppControllerAPI;
use AppDTOEntitiesMultipleRecords;
/**
* @param EntitiesMultipleRecords $data
*/
class ImportCSVAction {
public function __invoke(EntitiesMultipleRecords $data) {
dump($data);
return $data;
}
}
失败的请求:
curl -X 'POST'
'https://localhost:8000/api/dnas/import'
-H 'accept: application/json'
-H 'Content-Type: text/csv'
-d 'the content of dna_import.csv which is a correctly formed CSV string with ; delimiter'
您缺少一个自定义数据转换器来将传入的EntitiesMultipleRecords
转换为Dna
:
<?php
declare(strict_types=1);
namespace AppDataTransformer;
use ApiPlatformCoreDataTransformerDataTransformerInterface;
use ApiPlatformCoreSerializerAbstractItemNormalizer;
use AppEntityDna;
final class EntitiesMultipleRecordsDataTransformer implements DataTransformerInterface
{
/**
* @param object|array $data object on normalize / array on denormalize
*/
public function supportsTransformation($data, string $to, array $context = []): bool
{
if ($data instanceof Dna) {
return false;
}
return is_a($to, Dna::class, true) === $to && ($context['input']['class'] ?? null) === EntitiesMultipleRecords::class;
}
/**
* @param object $object
*
* @return object
*/
public function transform($data, string $to, array $context = [])
{
$dna = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE];
// Add everything from $data (EntitiesMultipleRecords) to $dna (Dna)
return $dna;
}
}
然后,您的操作可以接收到现在已完全填充的Dna
实例:
<?php
declare(strict_types=1);
namespace AppControllerAPI;
use AppEntityDna;
final class ImportCSVAction
{
public function __invoke(Dna $data): Dna
{
dump($data);
return $data;
}
}
如果您不需要对$data
做任何进一步的操作(目前已经准备好持久化),那么可以使用ApiPlatformCoreActionPlaceholderAction
。