(0)
(0)
(0)
(0)
Total: 0 DAY7: モデルとビューの操作
これまでのおさらい
ようやく6日間がおわったというところです。本チュートリアルの目的はsymfony1.0との違いを確認したいということだったのですが、sfFormを使う時点でかなり違ったものになった印象を感じてしまいます。
ただ、フォーム処理を切り分けるという意識がsymfony1.0のころに比べてはっきりしているため以前あったバリデーションをアクションでやりくりしなくてはいけない場合があった違和感はなくなりました。
総合的にみて、symfony1.2のsfFormはWebアプリケーションを開発するためにはフォームヘルパーよりも数段優れている印象を受けます。
さて、今日も本家のチュートリアルの内容をsymfony1.2で作成していきます。目標はモデルとビューの操作です。
プレファタリング(事前のリファクタリング)
本家チュートリアルではこのリファクタリングでパジネーションのHTML作成をヘルパーとして実装し直しています。
ヘルパーとはテンプレートで利用することを前提とした関数群のことで、今までもリンク先を指定してアンカータグを作成するlink_to関数などを利用してきました。
この機能そのものはsymfony1.2でも変わりません。
なので、本家チュートリアルと同様に以前作成したパジネーションをそのままヘルパーとして登録してみます。
まず、ヘルパー名をGlobalHelper.phpとします。
配置場所はアプリケーションのlib/helperディレクトリに配置します。
./apps/frontend/lib/helper/GlobalHelper.php
-
<?php
-
function pager_navigation($pager, $uri) {
-
$html = "";
-
if ($pager->haveToPaginate()) {
-
if ($pager->getPage() != 1) {
-
$html .= link_to(image_tag('previous.gif', 'align=absmiddle'), sprintf('%s?page=%d', $uri, $pager->getPreviousPage()));
-
}
-
for ($page_no = 1, $max = $pager->getLastPage(); $page_no <= $max; $page_no++) {
-
$html .= link_to_unless($page_no == $pager->getPage(), $page_no, sprintf('%s?page=%d', $uri, $page_no));
-
$html .= ($page_no != $pager->getLastPage()) ? ' ' : '';
-
}
-
if ($pager->getPage() != $pager->getLastPage()) {
-
$html .= link_to(image_tag('next.gif', 'align=absmiddle'), sprintf('%s?page=%d', $uri, $pager->getNextPage()));
-
$html .= link_to(image_tag('last.gif', 'align=absmiddle'), sprintf('%s?page=%d', $uri, $pager->getLastPage()));
-
}
-
}
-
return $html;
-
}
では、このヘルパーを呼び出すパーシャル(_list.php)でヘルパーの使用を宣言し、コードを置き換えます。
ヘルパーの宣言はコード中であればどこでも可能ですが、ファイルの先頭で行うようにルールを決めておくと解りやすくておすすめです。
./apps/frontend/modules/question/templates/_list.php
-
<?php use_helper('Text', 'Global', 'Date') ?>
-
....
-
<div id="question_pager">
-
</div>
画像も本家のリポジトリからもってくると本家のチュートリアルと同じようなパジネーションのできあがりです。
最新の質問一覧
最新の質問一覧ページを作成していきます。
名前はrecentアクションとします。
本家チュートリアルではPropelを使っているため少しばかり実装方法が異なります。
Propelの場合、ページャから質問データを取得することができるようです。
ただし、Doctrineのページャはページャを作成する途中でリストを取得できるようになっています。
そのため、recentアクションではページャとリストの両方をアサインするようにします。
./apps/frontend/modules/question/actions/actions.class.php
-
public function executeRecent(sfWebRequest $request)
-
{
-
$q = Question::createGetRecentDql();
-
$pager = new Doctrine_Pager($q, $request->getParameter('page'), sfConfig::get('app_pager_homepage_max'));
-
$this->questionList = $pager->execute();
-
$this->pager = $pager;
-
}
またDQLを作成するメソッドをQuestion.class.phpに用意します。
このときDoctrineのメソッドチェーンをうまく利用すれば以下のようにすっきりとかけます。
./lib/model/doctrine/Question.class.php
-
{
-
return self::createGetAllDql()
-
->orderby('created_at desc');
-
}
既に用意してある全取得用のDQLにOrderByの条件を追加したものを返しているだけです。
次に、テンプレートを用意します。recentアクションの結果ですのでrecentSuccess.phpになります。
ここで、以前作成したlistパーシャルを呼び出す事で表示部分を再利用しています。
また、パジネーションのリンク先をquestion/recentに変更しなければならないので、新しくruleという変数でパーシャルに渡すようにします。
./apps/frontend/modules/question/templates/recentSuccess.php
-
<h1>recent question</h1>
-
<?php include_partial('list', array('pager' => $pager, 'questionList' => $questionList, 'rule' => 'question/recent')) ?>
それに伴い、indexSuccess.php, _list.phpも修正しておきます。
./apps/frontend/modules/question/templates/indexSuccess.php
-
<?php include_partial('list', array('questionList' => $questionList, 'pager' => $pager, 'rule' => 'question/list')) ?>
./apps/frontend/modules/question/templates/_list.php
最新の回答一覧
次に最新の回答一覧を作成します。これは質問の一覧を作成したのと同じなので、以下実際の手順のみ記しておきます。
アンサーモジュールの作成
$ ./symfony generate:module frontend answer
recentアクションの実装
./apps/frontend/modules/answer/actions/actions.class.php
-
public function executeRecent(sfWebRequest $request)
-
{
-
$q = Answer::createGetRecentDql();
-
$pager = new Doctrine_Pager($q, $request->getParameter('page'), sfConfig::get('app_pager_homepage_max'));
-
$this->answerList = $pager->execute();
-
$this->pager = $pager;
-
}
アンサーモデルの実装
./lib/model/doctrine/Answer.class.php
アンサーテンプレートの作成
リレーション先の呼び出し方がPropelとDoctrineで微妙に異なっていたりします。
Doctrineではレコードにcountメソッドが用意されているので、$answer->getRelevancy()->count()というように呼び出しているところが特徴です。
./apps/frontend/modules/answer/templates/recentSuccess.php
-
<?php use_helper('Date', 'Global') ?>
-
<h1>recent answer</h1>
-
<div id="answers">
-
<?php foreach ($answerList as $answer): ?>
-
<div class="answer">
-
<h2><?php echo link_to($answer->getQuestion()->getTitle(), 'question/show?id='.$answer->getQuestion()->getId()) ?></h2>
-
<div>
-
</div>
-
</div>
-
<?php endforeach ?>
-
</div>
-
-
<div id="question_pager">
-
</div>
ブラウザで確認
http://symfony.centos5.localhost/askeet/frontend_dev.php/answer/recent
にアクセスして以下のようが画面がでればOKです。
ユーザープロフィール
つぎにユーザープロフィールの画面を作成していきます。
まずは表示するためのアクション(show)を実装していきます。
./apps/frontend/modules/user/actions/actions.class.php
-
public function executeShow(sfWebRequest $request)
-
{
-
$this->subscriber = User::findById($request->getParameter('id', $this->getUser()->getSubscriberId()));
-
$this->forward404Unless($this->subscriber->count());
-
$this->interests = $this->subscriber->getInterest();
-
$this->answers = $this->subscriber->getAnswer();
-
$this->questions = $this->subscriber->getQuestion();
-
}
ユーザーの情報をリクエストパラメータのIDから、なければログインユーザーのIDから取得しています。
この部分の本家チュートリアルとの違いはORMの違いによるものです。
次にテンプレート(showSuccess.php)を用意します。
./apps/frontend/modules/user/templates/showSuccess.php
-
-
<h2>Interests</h2>
-
-
<ul>
-
<?php foreach ($interests as $interest): $question = $interest->getQuestion() ?>
-
<?php endforeach; ?>
-
</ul>
-
-
<h2>Contributions</h2>
-
-
<ul>
-
<?php foreach ($answers as $answer): $question = $answer->getQuestion() ?>
-
<li>
-
</li>
-
<?php endforeach; ?>
-
</ul>
-
-
<h2>Questions</h2>
-
-
<ul>
-
<?php foreach ($questions as $question): ?>
-
<?php endforeach; ?>
-
</ul>
これで以下のようが画面が完成しました。
最後に誰からの質問なのかもわかるように以下のようなdivタグをquestion_body divの前に挿入しておきます。
./apps/frontend/modules/question/templates/showSuccess.php
./apps/frontend/modules/question/templates/_list.php
また、date_formatの書式を"yyyy-MM-dd"にして日本人が見たときに違和感がないようにしています。
ナビゲーションバーの追加
コンポーネントスロットというsymfonyのビューの仕組みを利用してナビゲーションバーを実装していきます。
ref: Definitive Guide 7章
一番の特徴は設定ファイルで表示内容を変更することができるというところですね。
本家と同じく、sidebarという名前でコンポーネントスロットを作成します。
レイアウトにコンポーネントスロットを追加
まず、レイアウトにコンポーネントスロットの関数を埋め込みます。
./apps/frontend/templates/layout.php
-
<div id="content_bar">
-
<?php include_component_slot('sidebar') ?>
-
<div class="verticalalign"></div>
-
</div>
コンポーネントスロットの定義を追加
コンポーネントスロットはview.ymlでどのコンポーネントを呼び出すかを定義できます。以下のようにsidebarモジュールのコンポーネントに定義されたdefaultメソッドを呼び出すようにします。
./apps/frontend/config/view.yml
-
default:
-
components:
-
sidebar: [sidebar, default]
sidebarコンポーネントを実装
コンポーネント(アクションの小さなもの)を用意します。
まず、sidebarモジュールが存在しないのでモジュールを作成します。
-
$ ./symfony generate:module frontend sidebar
そしてコンポーネントクラスにexecuteDefaultメソッドを用意します。
リンクを表示するだけなので、ロジックは必要ありません。そのためメソッドの中は空です。
symfony1.2からはアクションはメソッドの引数にsfWebRequestインスタンスを持つようになりましたが、これはコンポーネントでも同じです。
./apps/frontend/modules/sidebar/actions/components.class.php
-
<?php
-
class sidebarComponents extends sfComponents
-
{
-
public function executeDefault(sfWebRequest $request)
-
{
-
}
-
}
次にdefaultメソッドから呼ばれるテンプレートを用意します。テンプレートの名前はパーシャルと同じアンダーバーから始まるファイル名になります。
./apps/frontend/module/sidebar/templates/_default.php
最後に念のためにキャッシュをクリアしておきます。修正が反映されないときはsymfony cc!。これが基本です。
-
$ ./symfony cc
実際に画面をみてみましょう。
以下のようにサイドバーが表示されていればOKです。
ビューの設定
symfonyは設定ファイルが多いのが特徴です。
これがややこしくさせている理由でもありますが、一方でソースを編集しなくても動作を変えることができるという特徴があります。
本家チュートリアルではビューの設定をview.ymlで行えることを説明してくれています。
ただし、かならずしもview.ymlで設定を行う必要はないと思いと私は考えています。
たとえば、ページタイトルなどは各テンプレートで異なるでしょう。それを毎回view.ymlで設定しなくてはいけないというルールではさすがにデザイナーからは不満がでると思います。
デザイナーと分業する上ではできるだけテンプレートに関する作業はテンプレート内で終わらせるようにしてあげるように考慮することも大事かと思います。
なので、ここからview.ymlで設定を行いますが、デフォルトの設定のために用意するという感じでしょうか。
./apps/frontend/config/view.yml
-
default:
-
http_metas:
-
content-type: text/html
-
-
metas:
-
title: askeet!
-
description: 'symfony 1.2 で askeet! 実践中'
-
keywords: symfony, project, symfony1.2
-
language: ja
-
robots: index, follow
-
-
stylesheets: [main, layout]
-
-
javascripts: []
-
-
has_layout: on
-
layout: layout
-
-
components:
-
sidebar: [sidebar, default]
また明日
今日はここまでです。本家チュートリアルとは異なりDoctrineで実装していますが、リレーション先のデータ取得などはPropelより簡単な印象です。
ここまでのソースは以下のリポジトリからチェックアウトすることができます。
http://svn.1ms.jp/public/symfony/askeet12/tags/release_day_7
8日目はAJAXを利用してみます。
関連するその他の記事
Comments
Leave a Reply



