読者です 読者をやめる 読者になる 読者になる

OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

Symfony2での処理の流れについてまとめた

こんにちは!@mrtryです。 最近、スモークチーズのオイル漬けを作りました! チーズを燻製して、オリーブオイルにつけるだけでですが、とても美味しく、ハイボールが進みます。 燻製してみたいなぁ〜と、考えている人には、おすすめです!

さて、「Symfony2入門」2回目の投稿です。 前回は、全体としての基本動作をざっくり説明しました。 今回は、RequestオブジェクトからResponseオブジェクトを生成するまでの処理をもう少し掘り下げて行きたいと思います。

全体の処理の流れ

公式ドキュメントのフローチャートを参考に全体の処理の流れを追ってみましょう。 全体の処理の流れ

Symfonyでは、すべてのリクエストの処理が以下の流れに従っています。

  1. URLへのアクセスをフロントコントローラで受ける
  2. フロントコントローラ(app.php)が受けたリクエストからRequestオブジェクトを生成する
  3. カーネルがリクエストがあったURLをルータに知らせる
  4. ルータがURLにマッチするコントローラなどをまとめた「情報」をカーネルに返す
  5. カーネルがルータから受け取った情報を元にコントローラを実行する
  6. コントローラがResponseオブジェクトを返す

データを処理をするのはコントローラ、どのコントローラかを選択するのはルータ、コントローラとルータに依頼をするのがカーネル、と役割が分かれています。 それぞれについて、役割を確認していきます。

カーネル

カーネルは、クライアントからのRequestオブジェクトを受け取り、コントローラにそれを渡し、Response オブジェクトに変換します。 コードでは、フロントコントローラ(app.php(app_dev.php))にて呼びだされています。 気になった方は、前回の記事の終わりの方を見てみてください

ルータ

「このURLにアクセスがあったら、このコントローラを呼び出す」というURLのパスとコントローラを関連付け(マッピング)を行っています。 マッピングの設定は、設定ファイルかコントローラのPHPファイルに書くことで設定できます。

例として、前回も参考にしたsymfonyのデモアプリケーションの設定ファイルを見てみます。 設定ファイルは、app/config/routing.ymlにあります。 http://127.0.0.1:8000/にアクセスがあった時、 default/homepage.html.twigを引数に、FrameworkBundleTemplateControllerにあるtemplateActionを実行するという設定が書かれています。

homepage:
    path: /{_locale}
    requirements:
        _locale: '%app_locales%'
    defaults:
        _controller: FrameworkBundle:Template:template
        template:    default/homepage.html.twig
        _locale:     '%locale%'

コントローラ

コントローラは、Requestオブジェクトから情報を取得し、Responseオブジェクトを生成する関数群です。 ページのレンダリングやリダイレクト、画像などwebにアクセスして取得できる情報を任意にResponseオブジェクトとして返すことができます。

こちらも、実際にコントローラを見てみましょう。 見るコントローラは、先ほどルータで設定されていた、FrameworkBundleにあるTemplateControllerです。 vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.phpに実物があります。 このコントローラでは、引数で受け取ったファイルをレンダリングして、Responseとして返すという処理をしています。

<?php
namespace Symfony\Bundle\FrameworkBundle\Controller;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Response;

/**
 * TemplateController.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TemplateController implements ContainerAwareInterface
{
    use ContainerAwareTrait;

    /**
     * Renders a template.
     *
     * @param string    $template  The template name
     * @param int|null  $maxAge    Max age for client caching
     * @param int|null  $sharedAge Max age for shared (proxy) caching
     * @param bool|null $private   Whether or not caching should apply for client caches only
     *
     * @return Response A Response instance
     */
    public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null)
    {
        /** @var $response \Symfony\Component\HttpFoundation\Response */
        $response = $this->container->get('templating')->renderResponse($template);

        if ($maxAge) {
            $response->setMaxAge($maxAge);
        }

        if ($sharedAge) {
            $response->setSharedMaxAge($sharedAge);
        }

        if ($private) {
            $response->setPrivate();
        } elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) {
            $response->setPublic();
        }

        return $response;
    }

あわせて、実行時にレンダリングされるファイルを見てみましょう。 ルータの設定で、引数には、default/homepage.html.twigが設定されていました。 実際のファイルパスとしては、app/Resources/views/default/homepage.html.twigにあります。 http://127.0.0.1:8000/にアクセスされた際は、このファイルがレンダリングされて表示されます。

{% extends 'base.html.twig' %}

{% block body_id 'homepage' %}

{#
    the homepage is a special page which displays neither a header nor a footer.
    this is done with the 'trick' of defining empty Twig blocks without any content
#}
{% block header %}{% endblock %}
{% block footer %}{% endblock %}

{% block body %}
    <div class="page-header">
        <h1>{{ 'title.homepage'|trans|raw }}</h1>
    </div>

    <div class="row">
        <div class="col-sm-6">
            <div class="jumbotron">
                <p>
                    {{ 'help.browse_app'|trans|raw }}
                </p>
                <p>
                    <a class="btn btn-primary btn-lg" href="{{ path('blog_index') }}">
                        <i class="fa fa-users"></i> {{ 'action.browse_app'|trans }}
                    </a>
                </p>
            </div>
        </div>

        <div class="col-sm-6">
            <div class="jumbotron">
                <p>
                    {{ 'help.browse_admin'|trans|raw }}
                </p>
                <p>
                    <a class="btn btn-primary btn-lg" href="{{ path('admin_index') }}">
                        <i class="fa fa-lock"></i> {{ 'action.browse_admin'|trans }}
                    </a>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

例: トップページが表示されるまでの処理の流れ

実際に、ビルドインサーバを立ち上げて、http://127.0.0.1:8000/にアクセスすると、homepage.html.twigがレンダリングされたものが表示されます。

f:id:symmt9302:20160816104405p:plain

アクセスしてから、ページが表示するまでの流れを追うと以下のようになります。

  1. ユーザがhttp://127.0.0.1:8000/にアクセスする
  2. リクエストフロントコントローラ(app.php)が受け、カーネルにRequestオブジェクトを渡す
  3. カーネル/にアクセスがあったと、ルータに知らせる
  4. ルータが、設定ファイルを元にTemplateController.phpのtemplateActionを実行すれば良いと情報をカーネルに知らせる
  5. カーネルが情報にあったコントローラのアクションを実行する
  6. アクションした結果、ページがレンダリングされ、その情報はResponseオブジェクトとして生成される
  7. Responseオブジェクトを返す
  8. ユーザがページを見る

おわりに

今回は、Symfonyの処理流れについて、書きました。 次回は、ルータの設定方法について書きたいと思います。

参考