コントローラ

コントローラはあなたが作成する PHP コールバックです。それは、HTTP リクエストから情報を取得し、HTTP レスポンス(Symfony レスポンスオブジェクトとしての)を作成して返します。レスポンスは HTTP ページかもしれませんし、XML ドキュメントや、シリアライズされた JSON 配列、画像、リダイレクト、404 エラーになるかもしれません。もしくは、あなたが作り上げる何かかもしれません。 コントローラには、アプリケーションがページのコンテンツを表示するのに必要なロジックを詰め込みます。

Symfony コントローラにあるアクションを覗き込んで、それが、どれだけシンプルかを見てください。 次のサンプルは有名な Hello world! を表示するページをレンダリングしています。

use Symfony\Component\HttpFoundation\Response;

public function helloAction()
{
    return new Response('Hello world!');
}

コントローラの目的はいつも同じです。レスポンスオブジェクトを作成して、それを返すことです。 順を追っていくと、コントローラはリクエストから情報を読み込んだり、データベースのリソースを読み込んだり、メールを送ったり、セッションに情報をセットしたりするかもしれません。しかし、全ての場合において、コントローラは最終的にクライアントに配信されるレスポンスオブジェクトを返します。

以下に、よくある事例を示します。

  • コントローラ A は、このサイトのホームページのコンテンツを表すレスポンスオブジェクトを準備します。
  • コントローラ B は、データーベースからブログエントリを読み込む為に、リクエストから slug パラメータを読み込みます。そして、ブログを表示するレスポンスオブジェクトを作成します。もし、slug がデータベースに見つからなかった時は、404 ステータスコードと共にレスポンスオブジェクトを作成し、それを返します。
  • コントローラ C は、コンタクトフォームからの送信を取り扱います。コントローラはリクエストからフォームの情報を読み込み、コンタクト情報をデータベースに保存します。そして、コンタクト情報をあなたにメールします。最後に、コントローラは “Thank you” ページ(送信完了ページ)へクライアントのブラウザをリダイレクトするレスポンスオブジェクトを作成します。

リクエスト、コントローラ、レスポンスのライフサイクル

Symfony プロジェクトが取り扱う全てのリクエストは、同じシンプルなライフサイクルをたどります。 フレームワークが繰り返し行われる処理の面倒を見てくれるので、あなたが行うのは、コントローラの関数に自身のコードを書くことだけです。

  1. 各リクエストは1つのフロントコントローラファイル(例、web/app.phpweb/app_dev.php)によって、取り扱われます。このファイルはアプリケーションを起動します。
  2. ルーターがリクエストから情報(例、URI)を読み込みます。ルーターは情報にマッチするルートを検索します。そして、ルートから _controller パラメータを読み込みます。
  3. ルートにマッチしたコントローラが実行されます。そして、コントローラ内のコードはレスポンスオブジェクトを作成して、それを返します。
  4. HTTP ヘッダーとレスポンスオブジェクトのコンテンツはクライアントに送り返されます。

ページを作成することは、コントローラの作成(#3)や、コントローラへ URL をマップする為のルートを作成すること(#2)と同じくらい簡単です。

名前が似ていますが、「フロントコントローラ」はこの章で説明している「コントローラ」とは別の物です。 フロントコントローラは “web/” ディレクトリに置いてある、短い PHP ファイルです。そして、そのファイルを通して全てのリクエストは処理されます。 一般的なアプリケーションは本番環境用のフロントコントローラ(app.php)と開発環境用のフロントコントローラ(app_dev.php)を持っています。 多分、あなたがアプリケーションのフロントコントローラを編集したり、見たり、気にかける必要はないと思います。

シンプルなコントローラ

コントローラには PHP コールバック(関数、オブジェクトのクラス、クロージャ)の何れかがなることが出来るのですが、普通はコントローラクラス内のメソッドになります。コントローラはまた、アクションとも呼ばれています。

// src/AppBundle/Controller/HelloController.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;

class HelloController
{
    public function indexAction($name)
    {
        return new Response('<html><body>Hello '.$name.'!</body></html>');
    }
}

上の例の場合、コントローラは、コントローラクラス(HelloController)内にある indexAction メソッドであることに注意してください。 ネーミングで混乱しないでください。コントローラクラスは複数のコントローラ/アクションを一緒にグルーピングする為のシンプルで便利な方法です。 一般的に、コントローラクラスは複数のコントローラ/アクションを収容します。(例、updateAction, deleteAction 等々)

このコントローラは非常に簡単です。
※ 以下のコードのコメントに注目してください。

// src/AppBundle/Controller/HelloController.php

// コントローラクラス内で PHP のネームスペース機能を使用しています。
namespace AppBundle\Controller;

// Response クラスをインポートしています。(ネームスペース関連の機能です)
use Symfony\Component\HttpFoundation\Response;

// コントローラクラス名は "Controller" で終わるように命名します。これは一貫性を持たせるための規約です。
// このクラスは、ルーティング設定内で、クラス名の最初の部分("Hello")だけを使って参照されます。
class HelloController
{
    // アクション名は "Action" で終わるように命名します。
    // このアクションは、ルーティング設定内で、アクション名の最初の部分("index")だけを使って、参照されます。
    // 次のセクションでこのアクションに URI をマップするルートを作成します。
    // そして、どの様な仕組みで $name 引数を受け取るかを学びます。
    public function indexAction($name)
    {
        // レスポンスオブジェクトを作成して、返します。
        return new Response('<html><body>Hello '.$name.'!</body></html>');
    }
}

コントローラへの URL マッピング

新しいコントローラはシンプルな HTML ページを返します。 実際にこのページをブラウザで見るには、ルートを作成する必要があります。 ルートはコントローラに特定の URL をマップします。

// src/AppBundle/Controller/HelloController.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class HelloController
{
    /**
     * @Route("/hello/{name}", name="hello")
     */
    public function indexAction($name)
    {
        return new Response('<html><body>Hello '.$name.'!</body></html>');
    }
}
# app/config/routing.yml
hello:
    path:      /hello/{name}
    # uses a special syntax to point to the controller - see note below
    defaults:  { _controller: AppBundle:Hello:index }
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="hello" path="/hello/{name}">
        <!-- uses a special syntax to point to the controller - see note below -->
        <default key="_controller">AppBundle:Hello:index</default>
    </route>
</routes>
// app/config/routing.php
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->add('hello', new Route('/hello/{name}', array(
    // uses a special syntax to point to the controller - see note below
    '_controller' => 'AppBundle:Hello:index',
)));

return $collection;

これで、/hello/ryan(例、ビルトインサーバーの場合 http://localhost:8000/hello/ryan) にアクセスすることができます。 そして、Symfony は HelloController::indexAction() コントローラを実行し、$name 変数の為に、ryan をコントローラに渡します。 ページを作成することは、単にコントローラと関連するルートを作成することを意味します。

シンプルでしょ?

# AppBundle:Hello:index コントローラ書式

もし、YAML か XML フォーマットを使っていたら、コントローラを参照するのに、特別なショートカット書式である AppBundle:Hello:index を使ってください。コントローラ書式の詳細はコントローラのネーミングパターンを参照してください。

ルーティングシステムの詳細はルーティングの章で詳しく学べます。

コントローラの引数としてのルートパラメータ

あなたは既に、ルートが AppBundle の中にある、HelloController::indexAction() メソッドを指し示すことを知りました。 何より興味深いのは、そのメソッドに渡される引数です。

// src/AppBundle/Controller/HelloController.php
// ...
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

/**
 * @Route("/hello/{name}", name="hello")
 */
public function indexAction($name)
{
    // ...
}

コントローラは、ルートの{name} パラメータに対応した $name 引数を1つ持っています。 コントローラが実行された時、Symfony はルートのパラメータと引数を比べます。 そして、{name} の値が $name に引き渡されます。

以下の興味深い例を見てください。

// src/AppBundle/Controller/HelloController.php
// ...

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class HelloController
{
    /**
     * @Route("/hello/{firstName}/{lastName}", name="hello")
     */
    public function indexAction($firstName, $lastName)
    {
        // ...
    }
}
# app/config/routing.yml
hello:
    path:      /hello/{firstName}/{lastName}
    defaults:  { _controller: AppBundle:Hello:index }
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="hello" path="/hello/{firstName}/{lastName}">
        <default key="_controller">AppBundle:Hello:index</default>
    </route>
</routes>
// app/config/routing.php
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->add('hello', new Route('/hello/{firstName}/{lastName}', array(
    '_controller' => 'AppBundle:Hello:index',
)));

return $collection;

ここでは、コントローラは2つの引数を持つことができます。

public function indexAction($firstName, $lastName)
{
    // ...
}

コントローラの引数に、ルートパラメータをマッピングすることは、簡単で柔軟性があります。 開発中は、次のガイドラインを意識しておいてください。

# コントローラの引数の順番は関係ない

Symfony はルートのパラメータをコントローラの引数にマッチングします(名前でマッチング)。 コントローラの引数は完全に並べ替えすることができます。そして、それは完璧に動きます。

public function indexAction($lastName, $firstName)
{
    // ...
}

# コントローラの必須引数はルートのパラメータと一致する必要がある

次の例では RuntimeException 例外が発生します。何故なら、ルートに foo パラメータがないからです。

public function indexAction($firstName, $lastName, $foo)
{
    // ...
}

引数をオプションにすれば、問題は無くなります。次の例では例外は発生しません。

public function indexAction($firstName, $lastName, $foo = 'bar')
{
    // ...
}

# 全てのルートパラメータがコントローラの引数になる必要はない

例えば、lastName がコントローラで重要ではない場合、それを完全に省略することができます。

public function indexAction($firstName)
{
    // ...
}

全てのルートはまた、特別な _route パラメータを持っています。このパラメータにはマッチしたルートの名前と一致します。(先の例では hello) あまり使いませんが、これもコントローラの引数に使うことができます。また、ルートからコントローラの引数に引き渡せる他の変数もあります。 詳細はルートからコントローラへ追加情報を渡す方法を参照してください。

コントローラの引数としてのリクエスト

仮に、クエリーパラメータを読むことや、リクエストヘッダを読むこと、アップロードされたファイルにアクセスする必要があるとしたら? これらの情報は全て、Symfony のリクエストオブジェクトに格納されています。 コントローラでそれを取得するには、引数を追加して、Request クラスでタイプヒント(型を宣言)するだけです。

use Symfony\Component\HttpFoundation\Request;

public function indexAction($firstName, $lastName, Request $request)
{
    $page = $request->query->get('page', 1);

    // ...
}

詳細はリクエスト情報へのアクセスを参照してください。

基本コントローラクラス

利便性の為に、Symfony にはオプションの基本 Controller クラスが用意されています。 このクラスを継承することで、いくつかのヘルパーメソッドとコンテナを経由したサービスオブジェクトへのアクセスが出来るようになります。

コントローラクラスの上に use 文を追加して、基本 Controller を継承する為に HelloController を修正します。

// src/AppBundle/Controller/HelloController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class HelloController extends Controller
{
    // ...
}

これは実際には、コントローラーがどの様に機能するかについては何も変更しません。 基本コントローラクラスが提供するヘルパーメソッドへのアクセスを可能にしているだけです。 これらのヘルパーメソッドは Symfony のコア機能を使うための単なるショートカットです。 そのコア機能自体は基本コントローラクラスを使わなくても利用可能な物です。 実際のコア機能を知るための良い方法は、Controller クラスのソースを見てみることです。

もし、コントローラが基本コントローラクラスを継承しないで、Symfony のコア機能を使う方法に興味があるなら、サービスとしてのコントローラを参照してください。 これは必須ではありませんが、コントローラに注入するオブジェクトと依存関係をより厳密に制御することが出来るようになります。

リダイレクト

もし、ユーザーを他のページにリダイレクトしたい時は、redirectToRoute() メソッドを使用します。

public function indexAction()
{
    return $this->redirectToRoute('homepage');

    // redirectToRoute is equivalent to using redirect() and generateUrl() together:
    // return $this->redirect($this->generateUrl('homepage'), 301);
}

外部へリダイレクトしたい場合は、redirect() を使用して、URL を渡してください。

public function indexAction()
{
    return $this->redirect('http://symfony.com/doc');
}

デフォルトでは redirectToRoute() メソッドは 302(一時的)リダイレクトを実行します。 301(恒久的)リダイレクトを実行した時は、第3引数で変更します。

public function indexAction()
{
    return $this->redirectToRoute('homepage', array(), 301);
}

redirectToRoute() メソッドはユーザーをリダイレクトすることに特化したレスポンスを作成する、ただのショートカットです。 それは、以下のコードと同等です。

use Symfony\Component\HttpFoundation\RedirectResponse;

public function indexAction()
{
    return new RedirectResponse($this->generateUrl('homepage'));
}

テンプレートの表示

あなたが HTML を提供している場合、テンプレートをレンダリングすることをお勧めします。 render() メソッドはテンプレートをレンダリングして、Response オブジェクトのその内容を格納します。

// renders app/Resources/views/hello/index.html.twig
return $this->render('hello/index.html.twig', array('name' => $name));

あなたはまた、サブディレクトリの奥深くにテンプレートを格納することができます。 ただ、不必要に深い構造を作成しないようにしてください。

// renders app/Resources/views/hello/greetings/index.html.twig
return $this->render('hello/greetings/index.html.twig', array(
    'name' => $name
));

Symfony テンプレートエンジンは、テンプレートの章で非常に詳しく説明されています。

# バンドル内部にあるテンプレートの参照

あなたはまた、バンドルの内の Resources/views ディレクトリ内にテンプレートを置くこともできます。 そして、バンドル名:ディレクトリ名:ファイル名という書式でそれらを参照することができます。 例えば、AppBundle:Hello:index.html.twig は、src/AppBundle/Resources/views/Hello/index.html.twig を参照する事になります。 詳細は、バンドル内のテンプレートの参照を参照してください。

サービスへのアクセス

Symfony にはサービスと呼ばれる便利なオブジェクトがたくさん搭載されています。 これらはテンプレートのレンダリングやメールの送信、データベースの問い合わせ、あなたが定義する処理等で使われます。 新しいバンドルを追加すると、さらにサービスが追加されるはずです。

基本コントローラクラスを継承した時、あなたは get() メソッドを使って、Symfony のいかなるサービスへもアクセスすることができます。以下に、あなたが必要とする、いくつかの一般的なサービスです。

$templating = $this->get('templating');

$router = $this->get('router');

$mailer = $this->get('mailer');

他にどの様なサービスがあるでしょうか?全てのサービスを一覧表示するには、debug:continer コンソールコマンドを使用してください。

$ php bin/console debug:container

詳細は、サービスコンテナの章を参照してください。

エラーと404ページの管理

リクエストされた物が見つからなかった時には、あなたは HTTP プロトコルに従って、404 レスポンスを返すべきです。 これを行うには、特別なタイプの例外をスローしてください。もし、基本コントローラクラスを継承しているなら、次のように実行します。

public function indexAction()
{
    // retrieve the object from database
    $product = ...;
    if (!$product) {
        throw $this->createNotFoundException('The product does not exist');
    }

    return $this->render(...);
}

createNotFoundException() メソッドは特別な NotFoundHttpException オブジェクトを作成する為のショートカットです。 NotFoundHttpException オブジェクトは最終的に Symfony 内部で 404 HTTP レスポンスを引き起こします。

もちろん、あなたはコントローラ内でいかなる例外を発生されることも自由です。Symfony は自動的に 500 HTTP レスポンスコードを返すでしょう。

throw new \Exception('Something went wrong!');

全てのケースで、エラーページはエンドユーザーに表示され、デバッグエラーページは開発者に表示されます(app_dev.php を使っている時には - 環境を参照してください)。

あなたは、ユーザーが見るエラーページをカスタマイズしたいことと思います。それを行うには、エラーページのカスタマイズ方法を参照してください。

セッション管理

Symfony はリクエスト間でユーザー(ブラウザを使った実在の人物やボット、ウェブサービス等)についての情報を格納する為に使う、ナイスなセッションオブジェクトを提供します。デフォルトでは Symfony は PHP 標準のセッションを使って、クッキーに属性を保存します。

セッションからの情報の取得や保存はどのコントローラからでも、簡単に行うことができます。

use Symfony\Component\HttpFoundation\Request;

public function indexAction(Request $request)
{
    $session = $request->getSession();

    // store an attribute for reuse during a later user request
    $session->set('foo', 'bar');

    // get the attribute set by another controller in another request
    $foobar = $session->get('foobar');

    // use a default value if the attribute doesn't exist
    $filters = $session->get('filters', array());
}

これらの属性は、ユーザーのセッションが存続する間は保持されます。

フラッシュメッセージ

フラッシュメッセージと呼ばれる特別なメッセージをユーザーセッションに保存することもできます。 設計により、フラッシュメッセージは1度だけ使用されることを意図しています。 それらは、セッションから取り出されるとすぐに、自動的にセッションから削除されます。 この機能がユーザーへの通知を格納する為に、特に便利なフラッシュメッセージを可能にしています。

例えば、フォーム送信の処理をイメージしてみてください。

use Symfony\Component\HttpFoundation\Request;

public function updateAction(Request $request)
{
    $form = $this->createForm(...);

    $form->handleRequest($request);

    if ($form->isValid()) {
        // do some sort of processing

        $this->addFlash(
            'notice',
            'Your changes were saved!'
        );

        // $this->addFlash is equivalent to $this->get('session')->getFlashBag()->add

        return $this->redirectToRoute(...);
    }

    return $this->render(...);
}

リクエストを処理した後、コントローラはセッションにフラッシュメッセージをセットします。そして、リダイレクトをします。 メッセージキー(この例では、notice)は、何にすることも可能です。メッセージを取得する時に、このキーを使用します。

次のページテンプレート内で(ベースレイアウトの中であれば、より良いです)、セッションから全てのメッセージを読み込んでいます。

{% for flash_message in app.session.flashbag.get('notice') %}
    <div class="flash-notice">
        {{ flash_message }}
    </div>
{% endfor %}
<?php foreach ($view['session']->getFlash('notice') as $message): ?>
    <div class="flash-notice">
        <?php echo "<div class='flash-error'>$message</div>" ?>
    </div>
<?php endforeach ?>

フラッシュメッセージの異なるタイプのキーとして、notice, warning, error を使うのが一般的ですが、 ニーズに合わせて、好きなキーを使用することができます。

セッションにメッセージをキープしながらメッセージを取得するために、get() の代わりに peek() を使用することもできます。

レスポンスオブジェクト

コントローラの唯一の要件はレスポンスオブジェクトを返すことです。 Response クラスは HTTP のレスポンスを抽象化した物です。 HTTP のレスポンスとはクライアントに送り返されるヘッダーとコンテンツで満たされたテキストベースのメッセージのことです。

use Symfony\Component\HttpFoundation\Response;

// create a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);

// create a JSON-response with a 200 status code
$response = new Response(json_encode(array('name' => $name)));
$response->headers->set('Content-Type', 'application/json');

headers プロパティは HeaderBag オブジェクトです。そして、ヘッダーをセットしたり取得するいくつかのナイスなメソッドを持っています。 ヘッダー名は Content-Typecontent-type, content_type が同様に使えるように正規化されています。

特定の種類のレスポンスを簡単に作成する為の特別なクラスもあります。

レスポンスの種類クラス詳細の参照先
JSONJsonResponseJSON レスポンスの作成
ファイルBynaryFileResponseファイルの保存
ストリーミングレスポンスStreamedResponseストリーミングレスポンス

心配しないでください!コンポーネントドキュメントに、レスポンスオブジェクトに関するより多くの情報があります。 詳細はレスポンスを参照してください。

リクエストオブジェクト

ルーティングプレースホルダーの値に加えて、コントローラは、リクエストオブジェクトへのアクセスも可能です。 フレームワークは、コントローラに Request でタイプヒント(型宣言)された引数があれば、Request オブジェクトを注入します。

use Symfony\Component\HttpFoundation\Request;

public function indexAction(Request $request)
{
    $request->isXmlHttpRequest(); // is it an Ajax request?

    $request->getPreferredLanguage(array('en', 'fr'));

    $request->query->get('page'); // get a $_GET parameter

    $request->request->get('page'); // get a $_POST parameter
}

Response オブジェクトと同じように、リクエストヘッダーは HeaderBag オブジェクトに格納されて、簡単にアクセスできます。

心配しないでください!コンポーネントドキュメントにリクエストオブジェクトに関するより多くの情報があります。 詳細はリクエストを参照してください。

静的ページの作成

あなたはコントローラを作成すること無く、静的なページを作成することができます。(ルートとテンプレートのみ必要です)

詳細はコントローラなしでテンプレートを表示する方法を参照してください。

他のコントローラへの転送

あまり一般的ではありませんが、forward()メソッドを使って、内部で別のコントローラに転送することもできます。 ユーザーのブラウザーをリダイレクトする代わりに、それは、内部でサブリクエストを作成します。そして、コントローラをコールします。 forward()メソッドは、転送先コントローラから返されたレスポンスオブジェクトを返します。

public function indexAction($name)
{
    $response = $this->forward('AppBundle:Something:fancy', array(
        'name'  => $name,
        'color' => 'green',
    ));

    // ... further modify the response or return it directly

    return $response;
}

forward()メソッドがコントローラを表す特別な文字列を使用していることに注意してください(コントローラのネーミングパターンを参照)。 このケースでは、ターゲットとなるコントローラの関数は AppBundle 内の SomethingController::fancyAction() になります。 メソッドに渡される配列は、名前解決されたコントローラ(SomethingController::fancyAction())の引数になります。 これと同じアイデアがテンプレート内にコントローラを埋め込む時に使われています(コントローラの埋め込みを参照)。 ターゲットコントローラメソッドは次のようになります。

public function fancyAction($name, $color)
{
    // ... create and return a Response object
}

ルートの為のコントローラを作成する時と同じように、fancyAction の引数の順番は関係ありません。 Symfony は配列のキー名(例、name)とメソッドの引数名(例、$name)をマッチングします。 引数の順番を変えたとしても、Symfony はまだ、各変数に正しく値を渡します。

CSRF トークンのチェック

時々、Symfony のフォームコンポーネントを使わないアクションで CSRF 保護を行いたい時があります。 例えば、DELETE アクションを行っている時に、CSRF トークンをチェックする為に isCsrfTokenValid()メソッドを使うことができます。

if ($this->isCsrfTokenValid('token_id', $submittedToken)) {
    // ... do something, like deleting an object
}

// isCsrfTokenValid() is equivalent to:
// $this->get('security.csrf.token_manager')->isTokenValid()
//     new \Symfony\Component\Security\Csrf\CsrfToken\CsrfToken('token_id', $token)
// );

まとめ

ページを作成するたびに、最終的にそのページの為のロジックが含まれるいくつかのコードを書く必要があります。 Symfony では、これをコントローラと呼びます。そして、それはレスポンスオブジェクトを返す為の何らかの処理を行う PHP の関数です。 レスポンスオブジェクトは最終的にユーザーに返されます。

処理を簡単にする為に、基本コントローラクラスを継承することができます。 基本コントローラクラスは多くの一般的なタスクの為のショートカットメソッドを提供してくれます。 例えば、コントローラの中に HTML コードを置きたくないので、テンプレートからコンテンツをレンダリングして、返してくれる render()メソッドを使うことができます。

他の章で、以下の様なケースで、コントローラがどの様に使用されるかを見てください。

  • データベースへのオブジェクト永続化や読み込み
  • フォーム送信のプロセス
  • キャッシュの処理

クックブックの参照先