Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

SimpleApiBundleを作っている話

More than 1 year has passed since last update.

Symfony Advent Calendar 2018 3日目の記事です。
昨日は@77webさんのSymfony4のautowiringで狙ったinterfaceを確実に注入させる方法でした。

はじめに

一人でそこまで大規模ではないアプリケーションを作る事がおおいのですが、Symfonyで簡単なAPIを実装したくなる場合にどう実装したらいいのか?
結構悩むことが多いです。

要件的には以下のような感じです。

  • JSONでレスポンスする
  • POST時のrequestのbodyに{"name":"xxx","description":"yyy"}な形で入ってきたものを受け取る(bodyParser的なものが必要)
  • ControllerでAnnotationを使いAPIかどうかを指定したい

2つの方法

SymfonyでAPIな実装(jsonをレスポンスするような実装)をする場合に僕が思いつく方法が2つあります。

  1. JsonResponse()オブジェクトを返す
  2. FOSRestBundleを使う方法

JsonResponse()オブジェクトを返す

これは非常にシンプルな方法でコントローラで以下のように書くだけです。

<?php
class MainController
{
  public function index() 
  {
     return new JsonResponse(['message' => 'hello, world');
  }
}

確かにResponseをJSONで返すだけならこの方法で十分です。
しかし、POST時のデータの受取まで考えると、ちょっと微妙ですね。

FOSRestBundleを使う

SymfonyにはFOSRestBundleという素晴らしいBundleがあります。
https://github.com/FriendsOfSymfony/FOSRestBundle

ただ、今回の簡単なAPI実装ではちょっと機能が多すぎるのとAnnotationを活用出来ないのであまり積極的に使う気になれません。

SimpleApiBundle

polidog/SimpleApiBundle

最初に提示した3つの要件を満たすために作ったBundleです。

一番最初に求めたものは「@APIアノテーションを付けたら、jsonレスポンスする」ということでした。
@APIを外して@Templateに付け替えたら簡単にjsonからhtmlに切り替えられるみたいな。
すべてjsonで返すわけではなくて、一部はTwig使ってHTMLを返したいという要件がよくあって、それを簡単に実現したいのです。

使い方

README.mdを見ていただければすぐに分かると思いますが、単純にjsonのレスポンスを返すだけなら以下のとおりです。

// UserController.php

    /**
     * @Route("/user/{id}")
     * @Api()
     */
    public function me($id): array
    {
        $user = $this->userRepository->find($id);
        return [
            'id' => $user->getId(),
            'name' => $user->getUsername(),
            'avatar' => $user->getAvatar(),
        ];
    }

201レスポンス

annotationの引数statusCodeにステータスコードを指定するだけです。

    /**
     * @Route("/user/post", methods={"POST"})
     * @Api(statusCode=201)
     */
    public function post(Request $request): array
    {
        // TODO save logic.
        return [
            'status' => 'ok',
        ];
    }

ステータスコードの問題点

現状のSimpleApiBundleではannotationで宣言的に書かれているstatusCodeですが、動的に変化させたいケースもあります。
例えばバリデーションのチェックで失敗した場合は400でレスポンスコード返したいとか、実行時に決まるステータスコードのどう扱うべきか。

現状ではStatusCodeを変更する方法は一つしかなくてExceptionを発生させることです。
Exceptionのcodeが0以外は、ResponseオブジェクトのstatusCodeに反映するように実装しています。
POSTやGETを同じコントローラのアクションに記述するのは、アクションに複数の責務をもたせており、分離したほうがいいわけで、そう考えると、StatusCodeを変更するというのは、基本的にエラーが発生する場合のみであるべきなのではないでしょうか?

ということで現状は宣言的にStatusCodeを設定できるぐらいで十分な気もしてきました。

 最後に

Bundleを自分で作ると、Symfonyをより深く学ぶ事が出来ます。
SimpleApiBundleはかなりFOSRestBundleを参考にしていますし、僕はFOSRestBundleから多くのことを学びました。
年末年始にお時間のある方は小さなBundle作ってみてはいかがでしょか?

polidog
web系エンジニアです。
http://polidog.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away