(0)
(0)
(0)
(0)
Total: 0 DAY9: ローカルの改善
これまでのおさらい
symfonyで簡単なよくあるAJAXを実装するための関数や機能が用意されているのがわかりました。
また、外部ライブラリがコアのプラグインとして提供されるようになったということがわかりました。
9日目はテキストエリアのMarkdown対応と、ルーティングの変更です。
Markdown対応
MarkdownとはHTMLをwiki構文の1種で、HTMLをタグを書かなくても簡単に記述できる構文です。
詳しいことは本家チュートリアルとMarkdownのページを見てもらうとしてここは実装を行っていきます。
PHP版のMarkdownをダウンロードしてきて./lib/venderに配置しておきます。
./lib/vender/PHP Markdown 1.0.1m
-
./lib/vender/
-
└PHP Markdown 1.0.1m/
-
License.text
-
PHP Markdown Readme.text
-
markdown.php
あとはこのmarkdown.phpを読み込めばいいのですが、symfonyのインクルードパスに追加しておく必要があります。
そうすれば毎回パスを通す必要がないからです。
そこで、ProjectConfigration.class.phpにパスを追加するように書いておきます。
この書き方はsymfony1.2から利用できる書き方です。
./config/ProjectConfiguration.class.php
-
public function setup()
-
{
-
...
-
// add include path
-
sfConfig::get('sf_root_dir') . '/lib/vender/PHP Markdown 1.0.1m',
-
));
-
}
また、HTMLに変換したデータをhtml_bodyというカラムに保存するようにします。
そのために新しいカラムをスキーマに定義してテーブルの再構築を行っておきます。
./config/doctrine/schema.yml
-
Question:
-
columns:
-
....
-
html_body:
-
type: clob
-
default: ''
-
notnull: true
CREATE文の再作成、テーブルとモデルの再構成を行います。
-
$ ./symfony doctrine:build-model
-
$ ./symfony doctrine:build-sql
ではQuestionモデルにMarkdownで入力値を変換するようにかいておきます。
パスは通っているので、普通にrequre_onceを行うだけです。
./lib/model/doctrine/Question.class.php
-
public function setBody($v)
-
{
-
require_once('markdown.php');
-
$this->_set('body', $v);
-
// strip all HTML tags
-
$this->_set('html_body', markdown($v));
-
}
本家チュートリアルと異なるのは文字コードの設定をハードコーディングではなく、setting.ymlて定義したcharsetの値を利用している点です。
また、bodyやhtml_bodyなどのプロパティを上書きする場合はマジックメソッドを使わずに$this->setメソッドを使うようにしなければなりません。そうしないと永久ループによるセグメンテーションエラーなどが発生する場合があります。
では、本家と同じくテストデータを用意して読み込ませてみます。
./data/fixtures/test/002.question.yml
-
Question:
-
q1:
-
title: What shall I do tonight with my girlfriend?
-
User: fabien
-
body: |
-
We shall meet in front of the __Dunkin'Donuts__ before dinner,
-
and I haven't the slightest idea of what I can do with her.
-
She's not interested in _programming_, _space opera movies_ nor _insects_.
-
She's kinda cute, so I __really__ need to find something
-
that will keep her to my side for another evening.
-
q2:
-
title: What can I offer to my step mother?
-
User: anonymous
-
body: |
-
My stepmother has everything a stepmother is usually offered
-
(watch, vacuum cleaner, earrings, [del.icio.us](http://del.icio.us) account).
-
Her birthday comes next week, I am broke, and I know that
-
if I don't offer her something *sweet*, my girlfriend
-
won't look at me in the eyes for another month.
-
q3:
-
title: How can I generate traffic to my blog?
-
User: francois
-
body: |
-
I have a very swell blog that talks
-
about my class and mates and pets and favorite movies.
用意したデータを再読み込みします。
-
$ mysql -uyourname -p askeet <data/sql/drop.sql
-
Enter password:
-
$ mysql -uyourname -p askeet <data/sql/schema.sql
-
Enter password:
-
$ ./symfony doctrine:data-load
あとは表示するカラムをgetBodyではなくgetHtmlBodyに変更します。
./apps/frontend/modules/question/templates/showSuccess.php
-
<div class="question_body">
-
</div>
./apps/frontend/modules/question/templates/_list.php
-
<div class="question_body">
-
</div>
ここでWebアプリケーションの開発を正しく行える開発者の方は気づくはずです
出力時にHTMLをエスケープできていないのでは?
そうです。今のままではできていませんよね。
データを保存するときにHTMLに変換し、その値を出力するので安全だとはいえ、望ましい出力の方法は
デフォルトはエスケープされているようにして、エスケープしたくないときが例外処理のはずと
本家ではここでは何も触れていませんが、きちんと設定を行っておく事にします。
symfonyではデフォルトでは出力時のエスケープ処理を行わないようになっています。
ただし、標準でエスケープするようにsetting.ymlでescaping_strategyをOn指定するだけで対応できます。
-
all:
-
.settings:
-
# Output escaping settings
-
escaping_strategy: On
-
escaping_method: ESC_SPECIALCHARS
また、symfony1.2からは本当はアプリケーション作成時に以下のようにオプションで指定することができるのですが忘れてしまっていました。
-
$ ./symfony generate:app frontend --escaping-strategy=on
あとは、デフォルトでエスケープされるようになったので、html_bodyはエスケープさせないように設定します。
getHtmlBodyのメソッドに引数としてESC_RAWという定数を渡すだけなのです。簡単ですね。
ルーティングの改善
本日のもう1つの課題はルーティングを変えることです。
URLにidが含まれるのはSEO的にイケテないよねとのことからのようです。
ルーティングの設定方法そのものはsymfony1.0からと大きく変更はないので、そのままでいけると思います。
ユーザーidからnicknameに
userモジュールのshowアクションをnicknameで表示できるように変更します。
./apps/frontend/modules/user/actions/actions.class.php
-
public function executeShow(sfWebRequest $request)
-
{
-
$this->subscriber = User::findByNickname($request->getParameter('nickname'));
-
$this->forward404Unless($this->subscriber);
-
$this->interests = $this->subscriber->getInterest();
-
$this->answers = $this->subscriber->getAnswer();
-
$this->questions = $this->subscriber->getQuestion();
-
}
./lib/model/doctrine/User.class.php
-
{
-
$subscribers = Doctrine::getTable('User')->findByNickname($nickname);
-
return ($subscribers->count() == 1 )? $subscribers[0] : null;
-
}
URLにnicknameというキーがなくても済むようにルーティングを変更します。
また、本家のチュートリアルと同様にいくつかルーティングを追加しておきます。
./apps/frontend/config/routing.yml
-
# default rules
-
# quetion
-
homepage:
-
url: /
-
param: { module: question, action: index }
-
popular_questions:
-
url: /index/:page
-
param: { module: question, action: list, page: 1 }
-
recent_questions:
-
url: /recent/:page
-
param: { module: question, action: recent, page: 1 }
-
add_question:
-
url: /add_question
-
param: { module: question, action: add }
-
# answer
-
recent_answers:
-
url: /recent/answers/:page
-
param: { module: answer, action: recent, page: 1 }
-
# user
-
user_profile:
-
url: /user/:nickname
-
param: { module: user, action: show }
-
login:
-
url: /login
-
param: { module: user, action: login }
-
default_symfony:
-
url: /symfony/:action/*
-
param: { module: default }
-
default_index:
-
url: /:module
-
param: { action: index }
-
default:
-
url: /:module/:action/*
テンプレートのリンクの指定をルーティング名(@XXX)を利用する書き方に変更します。
./apps/frontend/modules/question/templates/showSuccess.php
./apps/frontend/modules/question/templates/_list.php
-
<div>asked by <?php echo link_to($question->getUser(), '@user_profile?nickname='.$question->getUser()->getNickname()) ?>
sidebarのリンク指定も変更しておきます。
./apps/frontend/modules/sidebar/templates/_default.php
このようにルーティング名を使ってリンクを簡単に指定できるところはsymfony1.2でも同じです。
また明日
新しいことはインクルードパスを追加するために便利なメソッドが用意されているぐらいでした。
今回のような場合ではわざわざ利用するまでもないかもしれませんね。
また、ルーティング名を使ったリンクの指定はできるだけ利用したほうが変更に強いアプリケーションになります。
ここまでのソースは以下のリポジトリからチェックアウトすることができます。
http://svn.1ms.jp/public/symfony/askeet12/tags/release_day_9
関連するその他の記事
Comments
Leave a Reply