php5フレームワークsymfonyを勉強しました。その2

前回の続き。今日は6章を勉強しました。
コントロールレイヤーの内側

アクションの情報を取り出す

class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // リクエストパラメータを取り出す
    $password    = $this->getRequestParameter('password');
 
    // コントローラ情報を取り出す
    $moduleName  = $this->getModuleName();
    $actionName  = $this->getActionName();
 
    // フレームワークのコアオブジェクトを取り出す
    $request     = $this->getRequest();
    $userSession = $this->getUser();
    $response    = $this->getResponse();
    $controller  = $this->getController();
    $context     = $this->getContext();
 
    // 情報をテンプレートに渡すためにアクション変数を設定する
    $this->setVar('foo', 'bar');
    $this->foo = 'bar';            // 短いバージョン
 
  }
}

アクションの終了方法

return sfView::SUCCESS;

エラービューの呼出し

return sfView::ERROR;

カスタムビューの呼出し

return 'MyResult';//actionNameMyResult.phpの呼出し

呼び出すビューが存在しない場合

return sfView::NONE;

レスポンスをechoしてsfView::NONEを返すことでビューを回避する

public function executeIndex()
{
  echo "<html><body>Hello, World!</body></html>";
 
  return sfView::NONE;
}
 
// 次のものと同等
public function executeIndex()
{
  return $this->renderText("<html><body>Hello, World!</body></html>");
}

ビューのレンダリングを回避してヘッダーのみを送信する

public function executeRefresh()
{
  $output = '<"title","My basic letter"],["name","Mr Brown">';
  $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');
 
  return sfView::HEADER_ONLY;
}

アクションを特定のテンプレートによってレンダリングしなければならない場合

$this->setTemplate('myCustomTemplate');

アクションが別のアクションにフォワードする場合

$this->forward('otherModule', 'index');

アクションがウェブリダイレクトの結果になる場合

$this->redirect('otherModule/index');
$this->redirect('http://www.google.com/');

forward404()メソッドの使い方

public function executeShow()
{
  $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
  if (!$article)
  {
    $this->forward404();
  }
}

forward404If()メソッドの使い方

public function executeShow()
{
  $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
  $this->forward404If(!$article);
}
 
// これも同じ
public function executeShow()
{
  $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
  $this->forward404Unless($article);
}

アクションのクラスの中でpreExecute、postExecuteとカスタムメソッドを使う

class mymoduleActions extends sfActions
{
  public function preExecute()
  {
    // ここに挿入されたコードはそれぞれのアクションコールの始めに実行される
    ...
  }
 
  public function executeIndex()
  {
    ...
  }
 
  public function executeList()
  {
    ...
    $this->myCustomMethod();  // アクションのクラスのメソッドがアクセス可能
  }
 
  public function postExecute()
  {
    // ここに挿入されたコードはそれぞれのアクションのコールの終わりに実行される
    ...
  }
 
  protected function myCustomMethod()
  {
    // "execute"で始まらない限り、独自のメソッドも追加できる
    // この場合、protectedもしくはprivateとしてこれらを宣言する方がベターである
    ...
  }
}

forward404If()メソッドの使い方

public function executeShow()
{
  $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
  $this->forward404If(!$article);
}

sfWebRequestオブジェクトのメソッド

getMethod() リクエストメソッド
getMethodName() リクエストメソッド名
getHttpHeader('Server') 任意のHTTPヘッダーの値
getCookie('foo') 名前付きクッキーの値
isXmlHttpRequest() Ajaxリクエストであるか?
isSecure() SSLリクエストであるか?
hasParameter('foo') リクエスト内にパラメータが存在するか?
getParameter('foo') 名前付きパラメータの値
getParameterHolder()->getAll() すべてのリクエストパラメータの配列
getUri() フルURI
getPathInfo() パス情報
getReferer()** リファラ
getHost() ホスト名
getScriptName() フロント・コントローラのパス名
getLanguages() 受信した言語の配列
getCharsets() 受信した文字セットの配列
getAcceptableContentTypes() 受信したcontent-typeの配列

アクションからsfRequestオブジェクトメソッドにアクセスする

class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    $hasFoo = $this->getRequest()->hasParameter('foo');
    $hasFoo = $this->hasRequestParameter('foo');  // 短いバージョン
    $foo    = $this->getRequest()->getParameter('foo');
    $foo    = $this->getRequestParameter('foo');  // 短いバージョン
  }
}

sfWebRequestオブジェクトは添付ファイルの扱い方を理解している

class mymoduleActions extends sfActions
{
  public function executeUpload()
  {
    if ($this->getRequest()->hasFiles())
    {
      foreach ($this->getRequest()->getFileNames() as $uploadedFile)
      {
        $fileName  = $this->getRequest()->getFileName($uploadedFile);
        $fileSize  = $this->getRequest()->getFileSize($uploadedFile);
        $fileType  = $this->getRequest()->getFileType($uploadedFile);
        $fileError = $this->getRequest()->hasFileError($uploadedFile);
        $uploadDir = sfConfig::get('sf_upload_dir');
        $this->getRequest()->moveFile($uploadedFile, $uploadDir.'/'.$fileName);
      }
    }
  }
}

ユーザーセッションにアクセスする

class mymoduleActions extends sfActions
{
  public function executeFirstPage()
  {
    $nickname = $this->getRequestParameter('nickname');
 
    // データをユーザーセッションに保存する
    $this->getUser()->setAttribute('nickname', $nickname);
  }
 
  public function executeSecondPage()
  {
    // デフォルトの値を持つユーザーセッションからデータを取り出す
    $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward');
  }
}

ユーザーのセッションからデータを削除する

class mymoduleActions extends sfActions
{
  public function executeRemoveNickname()
  {
    $this->getUser()->getAttributeHolder()->remove('nickname');
  }
 
  public function executeCleanup()
  {
    $this->getUser()->getAttributeHolder()->clear();
  }
}

テンプレートはユーザーのセッションの属性にもアクセスできる

<p>
  Hello, <?php echo $sf_user->getAttribute('nickname') ?>
</p>

flash属性

$this->setFlash('attrib', $value);

flash属性の表示

<?php echo $sf_flash->get('attrib') ?>

セッションの管理 セッションのクッキー名を変更する

apps/myapp/config/factories.ymlの中で、セッションのクッキー名を変更する

all:
  storage:
    class: sfSessionStorage
    param:
      session_name: my_cookie_name

アクセスの制限

apps/myapp/modules/mymodule/config/security.ymlの中の、アクセス制限の設定

read:
  is_secure:   off       # すべてのユーザーはreadアクションをリクエストできる

update:
  is_secure:   on        # update アクションは認証されたユーザーに対してのみ

delete:
  is_secure:   on        # adminクレデンシャルを持つ
  credentials: admin     # 認証されたユーザーのみ

all:
  is_secure:  off        # ともかくoffはデフォルト値

apps/myapp/config/settings.ymlの中でデフォルトのセキュリティアクションを定義する

all:
  .actions:
    login_module:           default
    login_action:           login

    secure_module:          default
    secure_action:          secure

ユーザーの認証ステータスを設定する

class myAccountActions extends sfActions
{
  public function executeLogin()
  {
    if ($this->getRequestParameter('login') == 'foobar')
    {
      $this->getUser()->setAuthenticated(true);
    }
  }
 
  public function executeLogout()
  {
    if ($this->getUser()->isAuthenticated())
    {
      $this->getUser()->setAuthenticated(false);
    }
  }
}

アクションの中でユーザーのクレデンシャルを処理する

class myAccountActions extends sfActions
{
  public function executeDoThingsWithCredentials()
  {
    $user = $this->getUser();
 
    // 1つもしくは複数のクレデンシャルを追加する
    $user->addCredential('foo');
    $user->addCredentials('foo', 'bar');
 
    // ユーザーがクレデンシャルを持つかどうかを確認する
    echo $user->hasCredential('foo');                      =>   true
 
    // ユーザーが1つのクレデンシャルを持つのか確認する
    echo $user->hasCredential(array('foo', 'bar'));        =>   true
 
    // ユーザーが両方のクレデンシャルを持つのか確認する
    echo $user->hasCredential(array('foo', 'bar'), false); =>   true
 
    // 1つのクレデンシャルを削除する
    $user->removeCredential('foo');
    echo $user->hasCredential('foo');                      =>   false
 
    // すべてのクレデンシャルをクリアする(ログアウト処理で便利)
    $user->clearCredentials();
    echo $user->hasCredential('bar');                      =>   false
  }
}

テンプレートの中のユーザーのクレデンシャルを処理する

<ul>
  <li><?php echo link_to('section1', 'content/section1') ?></li>
  <li><?php echo link_to('section2', 'content/section2') ?></li>
  <?php if ($sf_user->hasCredential('section3')): ?>
  <li><?php echo link_to('section3', 'content/section3') ?></li>
  <?php endif; ?>
</ul>

クレデンシャルの組み合わせ構文

editArticle:
  credentials: [ admin, editor ]              # admin AND editor

publishArticle:
  credentials: [ admin, publisher ]           # admin AND publisher

userManagement:
  credentials: [[ admin, superuser ]]         # admin OR superuser

バリデーションとエラー処理のメソッド

サンプルのバリデーションメソッド

class mymoduleActions extends sfActions
{
  public function validateMyAction()
  {
    return ($this->getRequestParameter('id') > 0);
  }
 
  public function handleErrorMyAction()
  {
    $this->message = "無効なパラメータ";
 
    return sfView::SUCCESS;
  }
 
  public function executeMyAction()
  {
    $this->message = "パラメータは正しい";
  }
}

フィルタチェーン

フィルタクラスの構造

class myFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    // この部分のコードは、アクションが実行される前に実行される(訳注:ファーストパス部分)
    ...
 
    // チェーンで次のフィルタを実行する
    $filterChain->execute();
 
    // この部分のコードは、アクションが実行された後、レンダリングが実行される前に実行される(訳注:セカンドパス部分)
    ...
  }
}

独自フィルタを開発する

apps/myapp/lib/rememberFilter.class.phpに保存された、サンプルのフィルタクラスのファイル

class rememberFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // このフィルタを1回だけ実行する
    if ($this->isFirstCall())
    {
      // フィルタはリクエストとユーザーのオブジェクトに直接アクセスできない。
      // これらを手に入れるためにcontextオブジェクトを使う必要がある
      $request = $this->getContext()->getRequest();
      $user    = $this->getContext()->getUser();
 
      if ($request->getCookie('MyWebSite'))
      {
        // ログイン
        $user->setAuthenticated(true);
      }
    }
 
    // 次のフィルタを実行する
    $filterChain->execute();
  }
}

apps/myapp/config/filters.ymlの中に保存される、サンプルのフィルタを有効にするファイル

rendering: ~
web_debug: ~
security:  ~

remember:                 # フィルタは独自の名前が必要
  class: rememberFilter
  param:
    cookie_name: MyWebSite
    condition:   %APP_ENABLE_REMEMBER_ME%

cache:     ~
common:    ~
flash:     ~
execution: ~

apps/myapp/lib/rememberFilter.class.phpの中で、パラメータの値を取得する

class rememberFilter extends sfFilter
{
  public function execute ($filterChain)
  {
      ...
      if ($request->getCookie($this->getParameter('cookie_name')))
      ...
  }
}

Google Analyticsのフィルタ

class sfGoogleAnalyticsFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // アクションの前は何もしない
    $filterChain->execute();
 
    // トラッカーコードでレスポンスを飾り付ける
    $googleCode = '
<script src="http://www.google-analytics.com/urchin.js"  type="text/javascript">
</script>
<script type="text/javascript">
  _uacct="UA-'.$this->getParameter('google_id').'";urchinTracker();
</script>';
    $response = $this->getContext()->getResponse();
    $response->setContent(str_ireplace('</body>', $googleCode.'</body>',$response->getContent()));
   }
}

セキュアなコミュニケーションフィルタ

class sfSecureFilter extends sfFilter
{
  public function execute($filterChain)
  {
    $context = $this->getContext();
    $request = $context->getRequest();
 
    if (!$request->isSecure())
    {
      $secure_url = str_replace('http', 'https', $request->getUri());
 
      return $context->getController()->redirect($secure_url);
      // フィルタチェーンを継続しない
    }
    else
    {
      // リクエストは既にセキュアなので、続けることができる
      $filterChain->execute();
    }
  }
}

apps/myapp/modules/mymodule/config/module.ymlの中の、モジュールの設定

all:                 # すべての環境用
  enabled:     true
  is_internal: false
  view_class:  sfPHP

enabledパラメータによってモジュールのすべてのアクションを無効にできます。すべてのアクションはmodule_disabled_module/module_disabled_actionアクションにリダイレクトされます(settings.ymlで定義)


symfonyをインストールするだけで、このようなアクションがデフォルトで使用出来るようになるというのは凄いですね。
使い慣れれば、ウェブアプリケーションならなんでもsymfonyで開発してしまうかもしれません。



[PR]Spreeの情報を集めています。

ECを持ちたい方、仕事でECを使いたい方向けのコミュニティサイトです。
このサイトでは世界で最も使用されているECの1つであるSpreeについての情報を提供しています。
http://spreecommerce.jp/