📑

Symfony + ApiPlatformでcsvレスポンスのヘッダーをカスタマイズする

2024/06/25に公開

apiPlatformはnormalizeするクラスを定義すればjson, csv, xmlなど様々な形式でレスポンスを返却することができる。
denormalizeする際、対象のオブジェクトのキー(csvならばカラムヘッダー)の名前はクラスのプロパティがそのまま使われる。
jsonで返却する場合はこれで問題ないが、csvで返却する場合は、カラムヘッダーは日本語の方がいいことがある。
この記事ではそれを実現する方法を紹介する。正しくないかもしれない。

方針

  1. カラムヘッダーを付与するようなattributeを定義する
  2. カスタムカラムヘッダーを設定したいクラスに1で定義したattributeを付与する
  3. normalizer (denormalizer) でattributeを参照してカラムヘッダーを取得し、csvのヘッダーに設定する

全体的なイメージは以下の通り。

<?php

// カラムヘッダーを設定するattribute
#[Attribute(Attribute::TARGET_PROPERTY)]
class CsvColumnNameAttribute
{
    public function __construct(
        private string $columnName
    ) {
    }
}

// csv で返却したいクラス
class OrderResource 
{
    #[CsvColumnNameAttribute('注文ID')]
    public int $id;

    #[CsvColumnNameAttribute('商品名')]
    public string $name;
}

これで以下のcsvが返却されるようにしたい。

注文ID,商品名
1,商品A

実装

attributeとcsvに変換するクラスの実装は前述の通りでよい。
次にnormalizer (denormalizer) を実装する。

<?php
class CsvResponseNormalizer implements NormalizerInterface
{
    private NormalizerInterface $normalizer;

    public function __construct(
        NormalizerInterface $normalizer,
    ) {
        $this->normalizer = $normalizer;
    }

    /**
     * @return mixed[]
     */
    public function normalize(mixed $object, ?string $format = null, array $context = []): array
    {
        if ($format !== 'csv') {
            return $this->normalizer->normalize($object, $format, $context);
        }

        $reflectionClass = new \ReflectionClass($object);
        $data            = [];

        foreach ($reflectionClass->getProperties() as $property) {
            $attributes                  = $property->getAttributes(CsvColumnNameAttribute::class);
            $attribute                   = $attributes[0]->newInstance();
            $property->setAccessible(true);
            $data[$attribute->columnName] = $property->getValue($object);
        }

        return $data;
    }

    public function supportsNormalization(mixed $data, ?string $format = null): bool
    {
        return $this->normalizer->supportsNormalization($data, $format);
    }

    /**
     * @return array<mixed>
     */
    public function getSupportedTypes(?string $format): array
    {
        return $this->normalizer->getSupportedTypes($format);
    }
}

あとはservices.yamlの登録する。

services:
    App\Shared\Presentation\Serializer\CsvResponseNormalizer:
        arguments: [ '@api_platform.serializer.normalizer.item' ]
        tags: [ 'serializer.normalizer' ]

これで返却されるcsvのヘッダーをカスタマイズすることができる

結論

symfony + apiPlatform + doctrine構成で実装しているが、これらが全部難しい。

GitHubで編集を提案

Discussion