如何实现从CSV文件创建多个资源



上下文

我正在尝试在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

最新更新