4日目: コントローラーとビュー ================================== .. include:: common/original.rst.inc | 今日は、昨日作成した基本的な ``jobController`` をカスタマイズします。 | そのための必要なコードの大部分はすでに Jobeet にあります。 * 全ジョブ一覧(``list`` )ページ * 新規ジョブ作成( ``create`` )ページ * 既存ジョブ更新( ``update`` )ページ * ジョブ削除( ``delete`` )ページ そのままのコードでも使用することはできますが、 `Jobeet mockups`_ に近づくようにリファクタリングします。 MVC アーキテクチャー -------------------- | 今日、ウェブ開発でコードを整理するための最も一般的な解決策は、 `MVC design pattern`_ です。 | MVC デザインパターンは、一言で言えば、コードを性質に応じて整理する方法です。 | このパターンは、 3 階層にコードを分離します。 * モデル層はビジネスロジックを定義します。(データベースはこのレイヤーに所属します) すでに Symfony には、すべてのクラスとファイルをバンドルの Entity/ ディレクトリ内のモデルに保持しています。 * ビューは、ユーザーと接する部分です。(テンプレートエンジンはこのレイヤーの一部です) Symfony 2.3.2 では、Viewレイヤーは主に ``Twig`` テンプレートで構成されています。 次の文節に出てきまが、それらは、さまざまな Resources/views/ ディレクトリに格納されています。 * コントローラは、クライアントに表示するためのビューに渡すデータを、モデルから取得するコードの一部です。 このチュートリアルの最初に Symfony をインストールしたときに、すべてのリクエストがフロントコントローラ(app.phpとapp_dev.php)によって管理されていることを見ました。 これらフロントコントローラはアクションに実際の作業を委任します。 レイアウト ---------- | `mockups`_ を詳しく見てみると、各ページはほとんど同じことに気づくでしょう。 | HTMLやPHPのコードが重複することはよくないことはご存知だと思います。 | そのため、共通の表示要素の重複を避けるような方法を見つける必要があります。 | この問題を解決する方法のひとつは、ヘッダとフッタを定義し、それらを各テンプレートから取り込むことです。 | さらにより良い解決方法としては、別の設計パターンである `decorator design pattern`_ を使用することです。 | デコレータデザインパターンは、問題を別の方法で、順番を逆にして解決します。 | テンプレートは、コンテンツがレイアウトと呼ばれる共通テンプレートによってレンダリングされた後に装飾されます。 | Symfony2 には、デフォルトのレイアウトが付随していないため、レイアウトを作成し、アプリケーションのページを修飾するために使用します。 | src/Ibw/JobeetBundle/Resources/views/ ディレクトリに新しいファイル layout.html.twig を作成し、次のコードを記入します。 src/Ibw/JobeetBundle/Resources/views/layout.html.twig .. code-block:: html {% block title %} Jobeet - Your best job board {% endblock %} {% block stylesheets %} {% endblock %} {% block javascripts %} {% endblock %}
{% for flashMessage in app.session.flashbag.get('notice') %}
{{ flashMessage }}
{% endfor %} {% for flashMessage in app.session.flashbag.get('error') %}
{{ flashMessage }}
{% endfor %}
{% block content %} {% endblock %}
Twig ブロック ------------- | Symfony のデフォルトのテンプレートエンジンである ``Twig`` では、ブロックを上記のように定義することができます。 | ``Twig`` ブロックはデフォルトのコンテンツを持つことが出来ます(例えば、 ``title`` ブロックを見てください)。 | それらを子テンプレートで置換または拡張することができます。 | 作成したレイアウトを利用するために、すべてのジョブのテンプレート( src/Ibw/JobeetBundle/Resources/views/Job/ の ``index``, ``edit``, ``new``, ``show``)の親テンプレート( ``layout.html.twig`` )の設定を拡張し、且つ、元の ``body`` ブロックの内容で ``content`` ブロックを上書きします。 .. code-block:: jinja {% extends 'IbwJobeetBundle::layout.html.twig' %} {% block content %} {% endblock %} スタイルシート/画像/JavaScript ------------------------------ | これはウェブデザインに関するものではないので、すでに Jobeet に使用するすべての必要なアセットを準備しました。 | :download:`download the image files ` アーカイブをダウンロード、解凍し、 src/Ibw/JobeetBundle/Resources/public/images/ ディレクトリに入れてください。 | :download:`download the stylesheet ` アーカイブをダウンロード、解凍し、 src/Ibw/JobeetBundle/Resources/public/css/ ディレクトリに入れてください。 | そして、以下のコマンドを実行します。 .. code-block:: bash $ php app/console assets:install web --symlink | Symfony にアセットを公開するよう指示します。 | css のフォルダ内を見ると、4つの css ファイル( admin.css 、 job.css 、 jobs.css 、 main.css )を持っていることがわかります。 | main.cssは、すべてのJobeetのページで必要なため、 ``layout.html.twig`` の中の stylesheet ブロック内に含めています。 | 残りは各ページごとに特化した css ファイルで、特定のページのみそれらを必要としています。 | テンプレートに新しい css ファイルを追加するには、 stylesheet ブロックを上書きします。 | ブロックの中で、新しい css ファイルを追加する前に、親を呼び出します(そのため、 main.css と追加の css ファイルを持っているでしょう)。 src/Ibw/JobeetBundle/Resources/views/Job/index.html.twig .. code-block:: jinja {% extends 'IbwJobeetBundle::layout.html.twig' %} {% block stylesheets %} {{ parent() }} {% endblock %} src/Ibw/JobeetBundle/Resources/views/Job/show.html.twig .. code-block:: jinja {% extends 'IbwJobeetBundle::layout.html.twig' %} {% block stylesheets %} {{ parent() }} {% endblock %} ジョブのホームページのアクション ------------------------------- | 各アクションはクラスのメソッドによって表されます。 | ジョブのホームページでは、クラスが ``JobController`` で、メソッドが ``indexAction()`` となります。 | 以下では、データベースからすべてのジョブを取得しています。 src/Ibw/JobeetBundle/Controller/JobController.php .. code-block:: php // ... public function indexAction() { $em = $this->getDoctrine()->getManager(); $entities = $em->getRepository('IbwJobeetBundle:Job')->findAll(); return $this->render('IbwJobeetBundle:Job:index.html.twig', array( 'entities' => $entities )); } // ... | コードを詳しく見てみましょう。 ``indexAction()`` メソッドは、すべての ``job`` を取得するために、 Doctrine のエンティティマネージャーを取得しています。 | エンティティマネージャーはデータベースからオブジェクトを取得し永続化する処理に責任を持ちます。 | そして、エンティティマネージャーからクエリーを作成するレポジトリを取得します。 | これは、テンプレート(ビュー)に渡される Job オブジェクトの Doctrine のクラス、 ``ArrayCollection`` を返します。 ジョブのホームページのテンプレート -------------------------------- index.html.twig テンプレートは、すべてのジョブの HTML テーブルを生成します。ここでは現在のテンプレートのコードは次のとおりです。 src/Ibw/JobeetBundle/Resources/views/Job/index.html.twig .. code-block:: html+jinja {% extends 'IbwJobeetBundle::layout.html.twig' %} {% block stylesheets %} {{ parent() }} {% endblock %} {% block body -%}

Job list

{% for entity in entities %} {% endfor %}
Id Type Company Logo Url Position Location Description How_to_apply Token Is_public Is_activated Email Expires_at Created_at Updated_at Actions
{{ entity.id }} {{ entity.type }} {{ entity.company }} {{ entity.logo }} {{ entity.url }} {{ entity.position }} {{ entity.location }} {{ entity.description }} {{ entity.howtoapply }} {{ entity.token }} {{ entity.ispublic }} {{ entity.isactivated }} {{ entity.email }} {% if entity.expiresat %}{{ entity.expiresat|date('Y-m-d H:i:s') }}{% endif%} {% if entity.createdat %}{{ entity.createdat|date('Y-m-d H:i:s') }}{% endif%} {% if entity.updatedat %}{{ entity.updatedat|date('Y-m-d H:i:s') }}{% endif%}
{% endblock %} それでは利用可能なのカラムのセットのみを表示するように整理してみましょう。 以下の ``twig`` のブロックの内容を置き換えます。 .. code-block:: html+jinja {% block content %}
{% for entity in entities %} {% endfor %}
{{ entity.location }} {{ entity.position }} {{ entity.company }}
{% endblock %} .. image:: /images/Day-4-2-jobs.png ジョブページテンプレート ---------------------- 今度は、ジョブページのテンプレートをカスタマイズしましょう。 show.html.twig ファイルを開き、次のコードを使用して、その内容を置き換えます。 src/Ibw/JobeetBundle/Resources/views/Job/show.html.twig .. code-block:: html+jinja {% extends 'IbwJobeetBundle::layout.html.twig' %} {% block title %} {{ entity.company }} is looking for a {{ entity.position }} {% endblock %} {% block stylesheets %} {{ parent() }} {% endblock %} {% block content %}

{{ entity.company }}

{{ entity.location }}

{{ entity.position }} - {{ entity.type }}

{% if entity.logo %} {% endif %}
{{ entity.description|nl2br }}

How to apply?

{{ entity.howtoapply }}

posted on {{ entity.createdat|date('m/d/Y') }}
Edit
{% endblock %} .. image:: /images/Day-4-individual-job.png ジョブページアクション ------------------------ ジョブページは show アクションによって生成されます。 ``JobController`` の ``showAction()`` メソッドで定義されます。 src/Ibw/JobeetBundle/Controller/JobController.php .. code-block:: php public function showAction($id) { $em = $this->getDoctrine()->getManager(); $entity = $em->getRepository('IbwJobeetBundle:Job')->find($id); if (!$entity) { throw $this->createNotFoundException('Unable to find Job entity.'); } $deleteForm = $this->createDeleteForm($id); return $this->render('IbwJobeetBundle:Job:show.html.twig', array( 'entity' => $entity, 'delete_form' => $deleteForm->createView(), )); } | indexアクションと同様に、``IbwJobeetBundle`` のリポジトリクラスは、``job`` を取得するために使用されます。ここでは、``find()`` メソッドを使用しています。 | このメソッドのパラメータは、``job`` の一意の識別子である主キーです。 | ``showAction()`` メソッドの ``$id`` パラメータに ``job`` の主キーが含まれている理由を次のセクションで説明します。 | データベースに指定したジョブが無い場合、$this->createNotFoundException() の例外を投げることで、ユーザーを 404 ページに転送します。 | 例外として、ユーザーに表示されるページは prod 環境と dev 環境で異なります。 .. image:: /images/Day-4-error1.png .. image:: /images/Day-4-error2.png 今日はこれですべてです!明日は、ルーティング機能に詳しくなりましょう。 .. seealso:: *Symfony2日本語ドキュメント* 豊富な日本語ドキュメントがありますので合わせて読み進めてみましょう。 * `ガイドブック »コントローラー `_ * `ガイドブック »ビュー `_ * `リファレンス »Twig関数リファレンス `_ * `リファレンス »Symfony2 Twig エクステンション `_ .. include:: common/license.rst.inc .. _`Jobeet mockups`: http://symfony.com/legacy/doc/jobeet/1_4/en/02?orm=Doctrine#chapter_02_the_project_user_stories .. _`MVC design pattern`: http://en.wikipedia.org/wiki/Model-view-controller .. _`mockups`: http://symfony.com/legacy/doc/jobeet/1_4/en/02?orm=Doctrine#chapter_02_the_project_user_stories .. _`decorator design pattern`: http://en.wikipedia.org/wiki/Decorator_pattern