コントローラーを作成する

コントローラーを作成する

ちょっとごまかしがありますが、私たちのゲストブックのプロジェクトは、本番サーバーで実際に動くようになりました。まだプロジェクトはページが一つもありません。ホームページは 404 エラーページとなっていますので、直してみましょう。

ホームページへ(http://localhost:8000/)のような、HTTP リクエストが来ると、 Symfony は リクエストされたパス (ここでは /) にマッチする ルート を探そうとします。 ルート は、リクエストのパスと対応する HTTP レスポンス を作成する関数の PHP の実行可能コード をリンクします。

これらの実行可能なコードを "コントローラー" と呼びます。 Symfony では、ほとんどのコントローラーは PHP のクラスで実装します。クラスは手動で作成することが可能ですが、もっと早くするため Symfony がやってくれることを見てみましょう。

Maker Bundle で楽をする

少ない努力でコントローラーを生成するのに symfony/maker-bundle パッケージを使用することができます。このパッケージは既に webapp パッケージの一部としてインストール済です。

Maker Bundle はたくさんのクラスを生成してくれます。この書籍では、常に使うことになります。各 "ジェネレーター" は、コマンドに定義されており、全てのコマンドは、 make コマンドのネームスペースにあります。

Symfony Console の list コマンドは、指定のネームスペース以下の全てのコマンドを一覧で表示します; Maker Bundle で使用可能なすべてのジェネレーターを調べてみてください:

1
$ symfony console list make

設定のフォーマットを選ぶ

プロジェクトの最初のコントローラーを作成する前に、使用したい設定のフォーマットを決める必要があります。Symfony は YAML, XML, PHP, PHPアトリビュート を最初からサポートしています。

関連するパッケージの設定 では、 YAML がベストな選択です。YAML フォーマットは、 config/ ディレクトリ内で使用されています。多くの場合、新しいパッケージをインストールすると、パッケージのレシピは .yaml という拡張子の新規ファイルをこのディレクトリに追加します。

PHP コードに関連する設定 では、コードに隣接して定義することができる アトリビュート がベターな選択です。例で説明しましょう。リクエストが来ると、設定は Symfony にどのリクエストパスがどのコントローラー(PHPクラス)によって処理するか伝える必要があります。YAML や XML や PHP フォーマットでは、2つのファイルを必要とします(設定ファイルと PHP のコントローラーファイル)。アトリビュートを使用すれば、設定はコントローラークラスで直接設定可能です。

インストールする必要のあるパッケージ名をどうやって判断するか疑問に思ったかもしれません。ほとんどの場合、知る必要はありません。Symfony はエラーメッセージ中にインストールが必要なパッケージを表示しています。 messenger パッケージのない状態で symfony console make:message コマンドを実行すると、正しいパッケージをインストールするヒントを含んだ例外を見ることができます。

コントローラーを生成する

make:controller コマンドで最初の コントローラー を作成しましょう:

1
$ symfony console make:controller ConferenceController

このコマンドは src/Controller ディレクトリ以下に ConferenceController クラスを作成します。生成されたクラスはちゃんと動くようなボイラープレートが既に入っています:

src/Controller/ConferenceController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ConferenceController extends AbstractController
{
    #[Route('/conference', name: 'conference')]
    public function index(): Response
    {
        return $this->render('conference/index.html.twig', [
            'controller_name' => 'ConferenceController',
        ]);
    }
}

#[Route('/conference', name: 'conference')] アトリビュートが、 ConferenceControllerindex() メソッドをコントローラにしています(設定は、コードに隣接しています)。

/conference をブラウザで開くと、このコントローラが実行され、レスポンスが返されます。

ホームページにマッチするようにルートを微調整します:

1
2
3
4
5
6
7
8
9
10
11
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -8,7 +8,7 @@ use Symfony\Component\Routing\Annotation\Route;

 class ConferenceController extends AbstractController
 {
-    #[Route('/conference', name: 'conference')]
+    #[Route('/', name: 'homepage')]
     public function index(): Response
     {
         return $this->render('conference/index.html.twig', [

コード内でホームページを参照したいときは、ルートの 名前 が便利です。 / パスをハードコードせずに、 ルート名を使いましょう。

デフォルトで表示されるページの代わりに、シンプルな HTML のページを返すようにしましょう:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -11,8 +11,13 @@ class ConferenceController extends AbstractController
     #[Route('/', name: 'homepage')]
     public function index(): Response
     {
-        return $this->render('conference/index.html.twig', [
-            'controller_name' => 'ConferenceController',
-        ]);
+        return new Response(<<<EOF
+<html>
+    <body>
+        <img src="/images/under-construction.gif" />
+    </body>
+</html>
+EOF
+        );
     }
 }

ブラウザを更新します:

/

コントローラーの主な責務は、リクエストに対応する HTTP レスポンス を返すことです。

この章の残りの部分は保存しないコードについて書いていますので、変更はこの時点でコミットしてしまいましょう:

1
2
$ git add .
$ git commit -m'Add the index controller'

イースターエッグを追加します

どうやってリクエストの情報からレスポンスが作られるかを見るために、 小さな Easter egg を追加してみましょう。ホームページに ?hello=Fabien のようなクエリー文字列が含まれていたら、挨拶ができるようにしてみましょう:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,17 +3,24 @@
 namespace App\Controller;

 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Annotation\Route;

 class ConferenceController extends AbstractController
 {
     #[Route('/', name: 'homepage')]
-    public function index(): Response
+    public function index(Request $request): Response
     {
+        $greet = '';
+        if ($name = $request->query->get('hello')) {
+            $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+        }
+
         return new Response(<<<EOF
 <html>
     <body>
+        $greet
         <img src="/images/under-construction.gif" />
     </body>
 </html>

Symfony は Request オブジェクトを通してリクエストされたデータを取得することができます。Symfony は、コントローラーの引数にこの型宣言があると、自動的に渡すことができます。 クエリー文字列にある name の値を取得して <h1> のタイトルに追加することができます。

ブラウザで、/ へアクセスして、それから /?hello=Fabien へ変えて、違いを見てみてください。

Note

XSS の問題を避けるために htmlspecialchars() が呼ばれているのに気づきましたか。適切なテンプレートエンジンへスイッチすると、自動的にサニタイズされます。

また、URL の一部の name を指定することができます:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -9,11 +9,11 @@ use Symfony\Component\Routing\Annotation\Route;

 class ConferenceController extends AbstractController
 {
-    #[Route('/', name: 'homepage')]
-    public function index(Request $request): Response
+    #[Route('/hello/{name}', name: 'homepage')]
+    public function index(string $name = ''): Response
     {
         $greet = '';
-        if ($name = $request->query->get('hello')) {
+        if ($name) {
             $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
         }

ルーティングの {name} の部分は、 ダイナミックな ルートパラメーター です。ワイルドーカードのようなものです。これでブラウザで /hello と開いてから hello/Fabien と変えても同じ結果が出すことができます。コントローラーに name と同じ引数($name)を指定することで、 {name} パラメーターの を取得することができます。

たったいま変更した部分をもとに戻します:

1
$ git checkout src/Controller/ConferenceController.php
1
2
$ git reset HEAD src/Controller/ConferenceController.php
$ git checkout src/Controller/ConferenceController.php

デバッグ中の変数

偉大なデバッグツールとしてSymfonyの dump() 関数があります。この関数はいつでも利用可能で、複雑な変数を見やすくてインタラクティブな形式で確認することができます。

リクエストオブジェクトをダンプするために src/Controller/ConferenceController.php を一時的に変更します:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,14 +3,17 @@
 namespace App\Controller;

 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Annotation\Route;

 class ConferenceController extends AbstractController
 {
     #[Route('/', name: 'homepage')]
-    public function index(): Response
+    public function index(Request $request): Response
     {
+        dump($request);
+
         return new Response(<<<EOF
 <html>
     <body>

ページを再読み込みしたら、"target" アイコンがツールバーに表示されていて、ダンプを確認することができます。クリックしてアクセスすると、シンプルになったナビゲーションを確認できます:

/

たったいま変更した部分をもとに戻します:

1
$ git checkout src/Controller/ConferenceController.php
1
2
$ git reset HEAD src/Controller/ConferenceController.php
$ git checkout src/Controller/ConferenceController.php
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.