symfony askeet Day 9

  • 質問と答えにリッチテキストを使えるようにする
  • すべてのIDを隠す
  • ルーティング

質問と答えにリッチテキストを使えるようにする

MarkdownというものはマークアップフォーマットをHTMLに変換するものらしい…
wikiとかはてな文法のようなものでしょうか。
それを使うためにはPHP Markdownを入れろとのことなので入れる。
http://www.michelf.com/projects/php-markdown/
zipファイルを解凍してmarkdown.phpを取り出して
askeet/libに入れる。
これで、後はrequire_onceで呼び出せばおk。
ただ、その都度markdown.phpで変換するとサーバに負荷が掛かるので
変換したものをDBに保存することにする。

ということでschema.ymlに追加する。
html_body:   { type: longvarchar }

追加するとこうなる。

  ask_question:
    _attributes: { phpName: Question, idMethod: native }
    id:          { type: integer, required: true, primaryKey: true, autoIncrement: true }
    user_id:     { type: integer, foreignTable: ask_user, foreignReference: id }
    title:       { type: longvarchar }
    body:        { type: longvarchar }
    interested_users: { type: integer, default: 0 }
    html_body:   { type: longvarchar }
    stripped_title: { type: varchar(255) }
    _uniques:
      unique_stripped_title: [stripped_title]
    created_at:  ~
    updated_at:  ~

追加したら、モデルを再構築して

symfony propel-build-model

SQL文作って

symfony propel-build-sql

SQLを更新する

symfony propel-insert-sql
setBodyが呼ばれたら問答無用で変換する。

askeet/lib/model/Question.phpにこれを追加する。

  public function setBody($v)
  {
    parent::setBody($v);

    require_once('markdown.php');

    // HTMLタグを外す
    $v = htmlentities($v,ENT_QUOTES,'UTF-8');

    $this->setHtmlBody(markdown($v));

  }

これでXSS対策もしつつ変換も行える。

テストデータを更新

askeetのサイトからそのままコピペして
askeet/data/fixtures/test_data.ymlに保存して

php batch/load_data.php
テンプレートを直す。

askeet/apps/frontend/templates/showSuccess.php

<div class="question_body">
  <?php echo $question->getHtmlBody() ?>
</div>

askeet/apps/frontend/templates/_list.php

    <div class="question_body">
    <?php echo truncate_text(strip_tags($question->getHtmlBody()), 200) ?>
    </div>


文面にリンクとか斜体とか出てきましたね。
ただ、文法が増えるのはいかがなものかなとも思います。

すべてのIDを書く

IDとnicknameは対になっているんだから
http://askeet.localhost/user/show/id/8
じゃなくて
http://askeet.localhost/user/crazyup
にしたいという話です。

まずはアクションを変更する

場所:askeet/apps/frontend/modules/user/

  public function executeShow()
  {
    $this->subscriber = UserPeer::retrieveByNickname($this->getRequestParameter('nickname'));
    $this->forward404Unless($this->subscriber);
    
    $this->interests = $this->subscriber->getInterestsJoinQuestion();
    $this->answers   = $this->subscriber->getAnswersJoinQuestion();
    $this->questions = $this->subscriber->getQuestions();
  }
モデルも修正する

場所:askeet/lib/model/UserPeer.class.php

  public static function retrieveByNickname($nickname)
  {
    $c = new Criteria();
    $c->add(self::NICKNAME,$nickname);

    return self::doSelectOne($c);
  }
リンクも直す

リンクを直すためにテンプレートを変更する。
場所:askeet/apps/frontend/modules/question/templates/showSuccess.php

<div>asked by <?php echo link_to($question->getUser(), 'user/show?nickname='.$question->getUser()->getNickname()) ?> on <?php echo format_date($question->getCreatedAt(), 'f') ?></div>

場所:askeet/apps/frontend/modules/question/templates/_list.php

<div>asked by <?php echo link_to($question->getUser(), 'user/show?nickname='.$question->getUser()->getNickname()) ?> on <?php echo format_date($question->getCreatedAt(), 'f') ?></div>

場所:askeet/apps/frontend/modules/answer/templates/recentSuccess.php

   posted by <?php echo link_to($answer->getUser(),'user/show?nickname='.$answer->getUser()->getNickname()) ?>
   on <?php echo format_date($answer->getCreatedAt(),'p') ?>

ほげっとなおしたら次。

ルーティングルールを追加する

場所:askeet/apps/frontend/config/routing.yml

user_profile:
  url:   /user/:nickname
  param: { module: user, action: show }

登録できたらsymfony ccでキャッシュをクリアする。

おーできた><

ルーティング

いい機会だから全部設定しちゃおうぜとaskeetが言っているのでやることに。
場所:askeet/apps/frontend/config/routing.yml

# question
question:
  url:   /question/:stripped_title
  param: { module: question, action: show }

recent_questions:
  url:   /question/recent/:page
  param: { module: question, action: recent, page: 1 }

popular_questions: 
  url:   /index/:page 
  param: { module: question, action: list } 

add_question:
  url:  /add_question
  param: { module: question, action: add }

# user
login: 
  url:   /login 
  param: { module: user, action: login } 

logout: 
  url:   /logout
  param: { module: user, action: logout } 

user_profile:
  url:   /user/:nickname
  param: { module: user, action: show }

# default rules
homepage:
  url:   /
  param: { module: question, action: list }

default_symfony:
  url:   /symfony/:action/*
  param: { module: default }

default_index:
  url:   /:module
  param: { action: index }

default:
  url:   /:module/:action/*

ルーティングをするといいことがあって
そうすると

   posted by <?php echo link_to($answer->getUser(),'@user_profile?nickname='.$answer->getUser()->getNickname()) ?>
   on <?php echo format_date($answer->getCreatedAt(),'p') ?>

こんな風に呼び出せる。
user/showが@user_profileで呼び出せる。
これは確かに便利。