【PHP】PHPフレームワーク「Flow」を試してみる～API作成編～

# 初めに
前回は[こちら](https://qiita.com/nnhkrnk/items/003a21f31bc8d0546c08)の記事でFlowの環境構築を実施しました。
今回はJson形式のレスポンスを返す簡単なAPIを作成してみたいと思います。

# さっそく作ってみた
プロジェクト構成は以下です（今回の話で出てこないものは割愛しました）。
```Neos.Welcome```の下にTestControllerを新しく作成していきます。

```
Quickstart
    └ Packages/
         ├ Application/
         |    └ Neos.Welcome/
         |         ├ Classes/
         |         |    └ Controller/
         |         |         ├ StandardController.php
         |         |         └ TestController（★）
         |         └ Configration/
         |              └ Routes.yaml
         ├ Framework/
         └ Libraries/
```
## １．Controllerを作成する

Controllerを作成していきます。
使えそうなflowコマンドがあるかどうか調べてみました。

```sh
$ ./flow help
Flow Application Framework 8.3.5 ("Development" context)

～中略～

PACKAGE "NEOS.KICKSTARTER":
-------------------------------------------------------------------------------
  kickstart:package                        Kickstart a new package
  kickstart:actioncontroller               Kickstart a new action controller
  kickstart:commandcontroller              Kickstart a new command controller
  kickstart:model                          Kickstart a new domain model
  kickstart:repository                     Kickstart a new domain repository
  kickstart:documentation                  Kickstart documentation
  kickstart:translation                    Kickstart translation
```

```kickstart```系のコマンドはファイルを自動で作成してくれるコマンドみたいですね。

```Controller```は以下の２種類がありますが、今回は```actioncontroller```のほうを使っていきます。

|Controllerの種類|説明|
|--|--|
|actioncontroller|APIとして使用するのController|
|commandcontroller|コマンドとして使用するController（らしい）|

ということで、```kickstart:actioncontroller```のコマンドの説明を見てみました。

```sh
$ ./flow help kickstart:actioncontroller

Kickstart a new action controller

COMMAND:
  neos.kickstarter:kickstart:actioncontroller

USAGE:
  flow.bat kickstart:actioncontroller [<options>] <package key> <controller name>

ARGUMENTS:
  --package-key        The package key of the package for the new controller
                       with an optional subpackage, (e.g.
                       "MyCompany.MyPackage/Admin").
  --controller-name    The name for the new controller. This may also be a
                       comma separated list of controller names.
```
どうやら作成する場所（パッケージ名）とController名を指定して作成するようです。
オプションも指定できるようですが、今回は指定なしでいきます。


ということで実行！
```sh
$ ./flow kickstart:actioncontroller Neos.Welcome Test
Created .../Neos.Welcome/Classes/Controller/TestController.php
Created .../Neos.Welcome/Resources/Private/Layouts/Default.html
Created .../Neos.Welcome/Resources/Private/Templates/Test/Index.html
```

Controller以外にhtmlファイルも作成されています。
```kickstart:actioncontroller```では、html形式のviewを返すControllerを作成してくれるみたいです。htmlファイルはそれですね。

作成された３つのソースは以下です。
```php:TestController
<?php
namespace Neos\Welcome\Controller;

/*
 * This file is part of the Neos.Welcome package.
 */

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class TestController extends ActionController
{


    /**
     * @return void
     */
    public function indexAction()
    {
        $this->view->assign('foos', array(
            'bar', 'baz'
        ));
    }
}

```

```html:Index.html
<f:layout name="Default" />

<f:section name="Title">Index view of Test controller</f:section>

<f:section name="Content">
    <h1>A freshly created Fluid template!</h1>
    <p>Some data set by the controller:</p>
    <ul>
        
        <f:for each="{foos}" as="foo">
            <li>{foo}</li>
        </f:for>
        
    </ul>
</f:section>

```

```html:Default.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title><f:render section="Title" /></title>
    </head>
    <body>
        <f:flashMessages class="flashmessages" />

        <h1><f:render section="Title" /></h1>
        <f:render section="Content" />
    </body>
</html>
```

## ２．Controllerにアクセスする
作成されたControllerにアクセスしてみましょう。
前回同様にサーバを立ち上げます。

```
$ ./flow server:run
Server running. Please go to http://127.0.0.1:8081 to browse the application.
[Sat Nov 18 16:25:15 2023] PHP 8.1.25 Development Server (http://127.0.0.1:8081) started
```

続いて先ほど作成したControllerにアクセスしてみます。
Flowでは、デフォルトのパスが以下のようになっているようです。

**http://~/[パッケージ名]/[Controller名]/[メソッド名]**
  * Controller名は「〇〇Controller」の〇〇の部分
  * メソッド名は「〇〇Action」の〇〇の部分

今回の場合、http://localhost:8081/Neos.Welcome/Test/index にアクセスすれば良さそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/a60b80bb-c100-1d02-ecbd-aa6969859caa.png)

ちゃんとindex.htmlの内容が表示されていますね！


## ３．Json形式のレスポンスを返す
それではjson形式のレスポンスを返してみましょう。
先ほど作成したControllerを以下のような形に修正しました。

```php: TestController
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class TestController extends ActionController
{

    // ポイント１：JsonViewを使うために、JsonViewをインジェクトする
    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @return void
     */
    public function indexAction()
    {
        // ポイント２：JsonViewにレスポンスをセットする
        //            キー値は'value'
        $this->view->assign('value', array(
            'bar', 'baz'
        ));
    }
}

```
修正のポイントは２つです。
### JsonViewクラスをインジェクトする
```JsonView```クラスはFlowで用意されているJson形式のレスポンス用Viewクラスです。
view用の変数にJsonViewクラスをインジェクトすることでJson形式のレスポンスを返すことができます。



```php
    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;
```

:::note warn
FlowではPHPDocの中でアノテーションを書くことでDIを設定することができます。
```@Flow\Inject``` や```@var```は意味のある実装なので、コメントと間違えて消さないように注意です。
:::



### assignメソッドを用いてレスポンスをセットする
assignメソッドを用いてレスポンスを設定します。
この際、キーの値は```value```でなければなりません。

```php
        $this->view->assign('value', array(
            'bar', 'baz'
        ));
```
設定は以上です。

実際にアクセスしてみると、Json形式のレスポンスを返すことが確認できました！
```sh
$ curl http://localhost:8081/Neos.Welcome/Test/index


StatusCode        : 200
StatusDescription : OK
Content           : ["bar","baz"]
RawContent        : HTTP/1.1 200 OK
                    Host: localhost:8081
                    Connection: close
                    X-Flow-Powered: Flow/8.3
                    Content-Length: 13
                    Content-Type: application/json
                    Date: Sun, 19 Nov 2023 06:55:01 GMT
                    X-Powered-By: PHP/8.1.25
                    ...
Forms             : {}
Headers           : {[Host, localhost:8081], [Connection, close], [X-Flow-Powered, Flow/8.3], [Content-Length, 13]...}     
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : System.__ComObject
RawContentLength  : 13
```



## ４．ルーティングを設定する
パスに任意の値を設定してみましょう。
まずは、現在のルーティング情報を確認してみます。

```sh
$ ./flow routing:list
Currently registered routes:
+---+------------------------------------------------+----------------+---------------------------------------------+
| # | Uri Pattern                                    | HTTP Method(s) | Name                                        |
+---+------------------------------------------------+----------------+---------------------------------------------+
| 1 | flow/welcome                                   | any            | Neos.Welcome :: Welcome screen              |
| 2 |                                                | any            | Neos.Welcome :: Redirect to welcome screen  |
| 3 | {@package}/{@controller}/{@action}(.{@format}) | any            | Neos.Flow :: default with action and format |
| 4 | {@package}/{@controller}(/{@action})           | any            | Neos.Flow :: default                        |
| 5 | {@package}                                     | any            | Neos.Flow :: default with package           |
| 6 |                                                | any            | Neos.Flow :: fallback                       |
+---+------------------------------------------------+----------------+---------------------------------------------+

```
現在は```flow/Welcom```のパスのみが設定されており、それ以外はデフォルトになっていそうです。

ルーティング情報はパッケージ直下にある```Configuration```ディレクトリの```Routes.yaml```に記載されています。
他に倣って、以下の内容を追記しました。

```yml
-
  name: 'Welcome screen'
  uriPattern: 'flow/welcome'
  defaults:
    '@package':    'Neos.Welcome'
    '@controller': 'Standard'
    '@action':     'index'
    '@format':     'html'

# 新規追加分
-
  name: 'Test Page'
  uriPattern: 'test/page'
  defaults:
    '@package':    'Neos.Welcome'
    '@controller': 'Test'
    '@action':     'index'
  httpMethods: ['GET']
```
```@format```は、「３．Json形式のレスポンスを返す」の手順でJsonViewをインジェクトしているため不要になります。

:::note info
JsonViewをインジェクトしてない場合、```@format: 'json'```を指定します。また、```index.html```と同じディレクトリに```index.json```を作成する必要があります。
:::


上記の設定後、ルーティングの設定が更新されていることが確認できました！

```sh
$ ./flow routing:list
Currently registered routes:
+---+------------------------------------------------+----------------+---------------------------------------------+
| # | Uri Pattern                                    | HTTP Method(s) | Name                                        |
+---+------------------------------------------------+----------------+---------------------------------------------+
| 1 | flow/welcome                                   | any            | Neos.Welcome :: Welcome screen              |
| 2 | test/page                                      | GET            | Neos.Welcome :: Test Page                   |
| 3 |                                                | any            | Neos.Welcome :: Redirect to welcome screen  |
| 4 | {@package}/{@controller}/{@action}(.{@format}) | any            | Neos.Flow :: default with action and format |
| 5 | {@package}/{@controller}(/{@action})           | any            | Neos.Flow :: default                        |
| 6 | {@package}                                     | any            | Neos.Flow :: default with package           |
| 7 |                                                | any            | Neos.Flow :: fallback                       |
+---+------------------------------------------------+----------------+---------------------------------------------+

```

アクセスも問題なしです！
```sh
$ curl http://localhost:8081/test/page


StatusCode        : 200
StatusDescription : OK
Content           : ["bar","baz"]
RawContent        : HTTP/1.1 200 OK
                    Host: localhost:8081
                    Connection: close
                    X-Flow-Powered: Flow/8.3
                    Content-Length: 13
                    Content-Type: application/json
                    Date: Sun, 19 Nov 2023 07:32:17 GMT
                    X-Powered-By: PHP/8.1.25
                    ...
Forms             : {}
Headers           : {[Host, localhost:8081], [Connection, close], [X-Flow-Powered, Flow/8.3], [Content-Length, 13]...}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : System.__ComObject
RawContentLength  : 13
```


# 終わりに
今回はFlowのControllerクラスの作成の仕方についてまとめました。
また何か分かったことあればいろいろアウトプットしていくつもりです。

# 参考
https://flowframework.readthedocs.io/en/stable/Quickstart/index.html