カルテット開発部の澤井です。

久しぶりのブログに、何を書こうかと色々と考えました。
悩んだ結果、やはり仕事で使う機会の多いSymfonyについて書こうと思います。

具体的には、Symfonyのイベントを使って、機能を拡張する簡単な例について書きます。

イベントについては、Built-in Symfony Events (Symfony Docs)に詳しい解説があります。

概要

本記事では、以下のような要望について考えます。

  • 通常のページでは、コントローラから返された連想配列を、Twigテンプレートを使って、HTMLのテーブルとして出力
  • 特定のページでは、コントローラから返された連想配列を、CSVファイルとしてダウンロード

本記事の目標は、イベントを使うことで、CSVファイルをダウンロードするという機能を一箇所で管理して、変更がしやすく、再利用性の高いコードを実現することです。

ちなみに、HTTPレスポンスヘッダに、以下の内容を指定することで、CSVファイルをダウンロードさせることができます。

  • Content-Typeヘッダに、text/csvを指定
  • Content-Dispositionヘッダに、attachmentを指定

コントローラの実装

まずは、@Templateアノテーションを使って、コントローラが返す連想配列を、テンプレートを使って表示します。

次章で、CSVファイルとしてダウンロードする機能を、イベントを使って実現します。

/**
 * @Route("/some")
 */
class SomeController extends Controller
{
    /**
     * @Route("/")
     * @Template
     */
    public function indexAction(Request $request)
    {
        // ...
        // $dataへ出力内容を保存する処理
        // ...

        return [ 'data' => $data ]
    }
    
    // ...

}

イベントの実装

CSVファイルをダウンロードする機能を、Symfonyのビルトインイベントであるkernel.viewイベントを使って実装します。

今回はサンプルなので、出力するデータは一次元配列かつカンマや改行などを含まないものとします。

namespace AppBundle\Listener;

// ...

class DownloadListener
{
    /**
     * @param FilterControllerEvent $event
     */
    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();
        if (!is_array($controller)) {
            return;
        }
        if ($controller[0] instanceof AssetController) {
            $event->getRequest()->attributes->set('is_download', true);
        }
    }

    /**
     * @param GetResponseForControllerResultEvent $event
     */
    public function onKernelView(GetResponseForControllerResultEvent $event)
    {
        if ($event->getRequest()->attributes->get('is_download')) {
            $controllerResult = $event->getControllerResult();
            $response         = new Response(
                implode(',', $controllerResult['data']),
                Response::HTTP_OK,
                [
                    'Content-Type' => 'text/csv',
                    'Content-Disposition' => 'attachment; filename=sample.csv',
                ]
            );
            $event->setResponse($response);
        }
    }
}

イベントを使うことで、CSVファイルをダウンロードする機能を、イベントリスナへ集約することができました。

その結果、他のページでCSVファイルをダウンロードする機能が必要になったときに、個別にコントローラへダウンロード機能を追加する必要はありません。

また、CSVファイルの文字コードを変換するといった、新たな要望が発生したときも、イベントリスナの修正だけで対応できます。

まとめ

今回は、イベントを使うことで、変更がしやすくて、再利用性の高いコードにすることができました。

Symfonyは、色々な場面でイベントを使います。 イベントに慣れて、上手に使うことが、Symfonyを使いこなすためのコツの1つかもしれません。

簡単な例でしたが、本記事でイベントの利点が伝われば嬉しいです。

以上、澤井でした。