symfony 1.2では何が新しくなったの?

(注意)

このページはWhat's new(日本語訳)の体裁を整え読みやすくした記事です。

このチュートリアルは手っ取り早くsymfony1.2のための技術的な紹介です。
すでにsymfony 1.0や1.1で作業をしたことがあり、symfony1.2の新しい機能について学びたい開発者のためのチュートリアルです。

最初に、symfony 1.1がPHP 5.1、symfony 1.0がPHP 5.0で動作していたのに対してsymfony1.2はPHP5.2.4かそれ以降に対応している事に注意してください。

もし、symfony 1.0 か 1.1からアップグレードしたいのであれば、symfonyをインストールしたディレクトリにあるUPGRADEファイル(http://www.symfony-project.org/installation/1_2/upgrade) ファイルを読んでください。
ここにsymfony 1.2 にプロジェクトを安全にアップグレードするために必要とされる情報のすべてがあります。

Propel

Propelはバージョン1.3にアップグレードされました。このバージョンではCreoleに代わりPDOがサポートされいたり、
多くの新しい機能が含まれています。新しい機能とはオブジェクトインスタンスプーリング、マスタースレーブコネクション、
入れ子集合のサポート、そしてより良い日付関連のハンドリングなどです。

`databases.yml`設定ファイルはPDO構文を使うようになりました。

CSS:
  1. /
  2. ....
  3.     dev:
  4.       propel:
  5.         param:
  6.           classname: DebugPDO
  7.  
  8.     all:
  9.       propel:
  10.         class: sfPropelDatabase
  11.         param:
  12.           dsn:        mysql:dbname=example;host=localhost
  13.           username:   username
  14.           password:   password
  15.           encoding:   utf8
  16.           persistent: true
  17.           pooling:    true
  18.           classname:  PropelPDO

トランザクションapiはわずかに変更が行われます。それは `->begin` は `->beginTransaction()`に、そして
`->rollback()`は`->rollBack()`に変更されることです。以下がその違いです。
`::doSelectRS`メソッドは `::doSelectStmt`に変更されました。

更なるアップグレードの詳細についてはhttp://propel.phpdb.org/trac/wiki/Users/Documentation/1.3/Upgradingを、
完全なドキュメントについてはhttp://propel.phpdb.org/trac/wiki/Users/Documentation/1.3を見てください

Doctrine

Doctrineはsymfony 1.2 では第1級の市民です。
symfony 1.2 では Propelプラグインと Doctrineプラグインの両方をバンドルしているということです。
全ての組み込まれた機能はPropelとDoctrineの両方で利用できます。
あなたにより適したORマッパーを自由に選択してください。

リクエスト(Request)

ブラウザからの `PUT` と `DELETE` リクエストを `POST` メソッドと特別な `sf_method` パラメータを追加することで
シミュレーションが可能になりました。

PHP:
  1. ...
  2.     <form action="#" method="POST">
  3.       <input type="hidden" name="sf_method" value="PUT" />
  4.  
  5.       <!-- // ... -->
  6.     </form>

`form_tag()` ヘルパーは `GET` と `POST` と異なるメソッドのために自動的にhiddenタグを生成します。
そのため、formの開始タグは次のように `form_tag()` ヘルパーを使うことで生成できます。

PHP:
  1. ...
  2.     <?php echo form_tag('#', array('method' => 'PUT')) ?>

フォーム(Forms)

フォームの入れ子

symfony1.1ではPropelでのフォームの主な制限の1つとして組み込まれたフォームからオブジェクトを自動保存することができないことでした。
symfony1.2では実装されるようになりました。これはPropelのフォームを保存するとき、symfonyによって自動的に主となるオブジェクトと
関連する入れ子になったフォームの全てをオブジェクトを保存してくれるということです。

新しい `sfForm` メソッド

新しい `sfForm::renderFormTag()` メソッドは `form`開始タグを現在のフォームのために生成します。
また、フォームがマルチパートを必要とする場合は `enctype` 属性も追加しますし、
メソッドが `GET`または`POST`以外の場合はhiddenタグを追加します。

PHP:
  1. ...
  2.     <?php echo $form->renderFormTag('@article_update', array('method' => 'PUT')) ?>

`sfFormPropel` クラスは 関連するオブジェクトに基づくHTTPメソッドを自動的に変更するために
`renderFormTag()`を上書きします。つまり、オブジェクトが新しければ、メソッドは`POST`であり
オブジェクトが既に存在すればメソッドは`PUT`になるでしょう。

`sfForm` クラスはformにエラーがあればfalseを返し、
そうでなければtrueを返す新しい `hasErrors()` メソッドを返します。

このメソッドはフォームに値が結びつけられていない場合は `false` を返します。
このメソッドはテンプレートでとても役に立ちます。

PHP:
  1. ...
  2.     <?php if ($form->hasErrors()): ?>
  3.       The form has some errors you need to fix.
  4.     <?php endif; ?>

新しい `renderUsing` メソッドが `sfForm` クラスに追加されました。このメソッドによって
レンダー処理されるフォームが特定のwidgetスキーマのフォーマッターを使うことを許可します。
これによってテンプレートにおいて直接フォーマッターを使うことができるようになります。

PHP:
  1. ...
  2.     // テンプレートにおいて, フォームは"list"というフォームフォーマットで描画されます。
  3.     echo $form->renderUsing('list');

`sfForm::renderHiddenFields()`メソッドはhiddenウィジットのためのHTMLを返します。
フォームのフィールドをテンプレート内で見えないようにしたいときに役に立ちます。

PHP:
  1. ...
  2.     <form action="<?php echo url_for('@some_route') ?>">
  3.       <?php echo $form->renderHiddenFields() ?>
  4.       <ul>
  5.         <?php echo $form['name']->renderRow() ?>
  6.       </ul>
  7.       <input type="submit" />
  8.     </form>

フォームは `getStyleSchees()`と`getJavaScripts()`メソッドという
フォームを表示するときに必要となるスタイルシートとJavascriptのファイルを返すメソッドを持つようになりました。
この2つのメソッド上書きすることで、またはより一般的に各ウィジットを使うことで
これらのファイルをフォーム自身のなかに定義しておくことができます。
(詳しくはWidgetの項目を確認してください)

テンプレート中では`include_javascripts_for_form()`と`include_stylesheets_for_form()`ヘルパーを利用できます。
これら両方とも引数としてフォームオブジェクトを渡します。

PHP:
  1. ...
  2.     <?php include_javascripts_for_form($form) ?>
  3.     <?php include_stylesheets_for_form($form) ?>

`sfForm`はより簡単にテンプレート内でフォームのフィールドを繰り返しやすくできるように`Iterator`と`Countable`インターフェースを実装しています。

PHP:
  1. ...
  2.     <ul>
  3.       <?php foreach ($form as $field): ?>
  4.         <li><?php echo $field ?></li>
  5.       <?php endforeach; ?>
  6.     </ul>

前処理で値をクリーンアップ

オブジェクトを更新して、Propelによって値が利用される前に値をクリーニングできる前処理の方法が追加されました。

もし値のための前処理を行いたいのであれば、`updateXXXColumn()` メソッドを XXXがPropelカラムのPHP名である部分の
formに追加する必要があります。

このメソッドは 処理された値かクリーンアップされた値の配列から値を取り除くために `false` を返さなければなりません。

PHP:
  1. ...
  2.     class UserForm extends sfFormPropel
  3.     {
  4.       // ...
  5.  
  6.       protected function updateProfilePhotoColumn($value)
  7.       {
  8.         // if the user has not submitted a profile_photo,
  9.         // remove the value from the values as we want
  10.         // to keep the old one
  11.         if (!$value)
  12.         {
  13.           return false;
  14.         }
  15.  
  16.         // remove the old photo
  17.         // save the photo on the disk
  18.  
  19.         // change the value to the new file name
  20.         return $filename;
  21.       }
  22.     }

バリデーター(Validators)

`sfValidatorSchemaCompare`

`sfValidatorSchemaCompare`定数は変更されました。コードに変更は必要ありません。しかしこれからは、
すばらしいショートカットを使う事ができます。
次の2つの例が実装例です。

PHP:
  1. ...
  2.     // symfony 1.1 と 1.2での記述方法
  3.     $v = new sfValidatorSchemaCompare('left', sfValidatorSchemaCompare::EQUAL, 'right');
  4.  
  5.     // symfony 1.2 での記述方法
  6.     $v = new sfValidatorSchemaCompare('left', '==', 'right');

`sfValidatorChoice`

`sfValidatorChoice`は `sfValidatorChoiceMany`にある `multiple` オプションと同じ振るまいをするために
`multiple`オプションを持つよようになりました。

`sfValidatorPropelChoice`は `sfValidatorPropelChoiceMany`にある `multiple` オプションと同じ振るまいをするために
`multiple`オプションを持つよようになりました。

`sfValidatorDateRange`

symfony 1.2は日付の期間を検証するための新しい`sfValidatorDateRange`バリデータを備えています。

`sfValidatorFile`

`sfValidatedFile`クラスはファイルを保存するときに少しばかり柔軟になりました。

このコンストラクターは新しい引数をとることができ、ファイルを保存するときに利用するパスを渡すことができます。

それで、ファイルを保存するとき、次のようにすることができます。

  • これまでのように絶対パスでファイル名を渡す
    PHP:
    1. $file->save(sfConfig::get('sf_uploads_dir').'/filename.pdf');

  • 相対パスを渡す(symfonyはパスを先頭に加えることで絶対パスを作成します)
    PHP:
    1. $file->save('filename.pdf');

  • `null`を渡す。この場合ファイル名は`generatedFilename()`メソッドによって自動的に生成されます。
    PHP:
    1. $file->save();

さらに、 `save()` メソッドは(引数のパスに相対的な)保存されたファイルのファイル名を返すようになりました。

`sfValidatedFile`オブジェクトは `sfValidatorFile` バリデーターによって生成されるので、
新しい`path`オプションは後者のクラスに追加され `sfValidatedFile`コンストラクターにただ渡されるだけです。

PHP:
  1. $this->validatorSchema['file'] = new sfValidatorFile(array('path' => sfConfig::get('sf_uploads_dir')));

`sfFormPropel`はこれらの新しい改良部分をPropelオブジェクトに関連するファイルの保存を自動化することで活用することができます。
それが新しい`saveUploadedFile()`メソッドです。

そのため、もし`file`カラムを持つPropelオブジェクトがあり、フォームクラスで`file`バリデーターを定義するときパスを渡しているなら
symfonyはあなたの代わりに全ての処理を行ってくれます。

  • もしフォームにファイルがアップロードされていないのなら
    • * 何もせずにデータベースにも変更を加えません
  • もしファイルがアップロードされたなら
    • 古いファイルを削除
    • 新しいファイルを保存
    • `file`カラムにファイル名をセット(与えられたパスへの相対)

それで、関連するアクションにおいて、ただ`$this->form->save()`とするだけで自動的にオブジェクトを保存しファイルを保存します。

標準では、symfonyはハッシュと推測した拡張子を追加してユニークなファイル名を生成します。
もしファイル名を変更したいなら`generateXXXFilename()` メソッドをオブジェクト内に作成するだけです。
このメソッドは`sfValidatedFile`オブジェクトに与えられています。

PHP:
  1. ...
  2.     public function generateFileFilename(sfValidatedFile $file)
  3.     {
  4.       return $this->getId().$file->getExtension($file->getOriginalExtension());
  5.     }

もちろん さきほど説明したように`updateXXXColumn()`をこの振る舞いを上書きするために作成し、
あなた自身でファイルのアップロードの処理をマージする事も可能です。

`sfValidatorI18nChoiceCountry` と `sfValidatorI18nChoiceLanguage`

symfony1.1では`sfValidatorI18nChoiceCountry`と`sfValidatorI18nChoiceLanguage`バリデーターは
`culture`オプションが必須でした。この`culture`はこれらのバリデーターで使われていないので、
`culture`オプションは廃止される予定です。後方互換性のためにまだ残っていますが、もう使う必要はありません。

メッセージに関連する`required`と`invalid`の標準のエラーコードの設定

2つの新しいメソッドが`sfValidatorBase`クラスに追加され、標準時の`required`と`invalide`エラーコードのための
メッセージを定義できるようになりました。

PHP:
  1. sfValidatorBase::setRequiredMessage('This field is required');
  2. sfValidatorBase::setInvalidMessage('The value provided for this field is invalid');

ウィジット(Widgets)

Widget オプション

全てのwidgetsは新しい`default`オプションを持っています。このオプションはwidgetの
標準の値をセットします。

PHP:
  1. $widget = new sfWidgetFormInput(array('default' => 'default value'));
  2.     $widget->setDefault('default value');
  3.     echo $widget->getDefault();

widgetスキーマに標準の値をセットすることもできるようになりました。

PHP:
  1. ...
  2.     $widget = new sfWidgetFormSchema(...);
  3.     $widget->setDefaults(array('name' => 'default value'));
  4.     $widget->setDefault('name', 'default value');
  5.     var_export($widget->getDefaults());

フォームの標準値で上書きしないときこれらの標準に設定した値はそのことを考慮します

すべてのwidgetは新しい`label`オプションを持っています。このオプションは
各widgetにラベルを設定します。

PHP:
  1. ...
  2.     $widget = new sfWidgetFormInput(array('label' => 'Enter your name here'));
  3.     $widget->setLabel('Enter your name here');
  4.     echo $widget->getLabel();

Widgets JavaScripts と stylesheets

Widgetは`getJavascripts()`と`getStyleSheets()`メソッドで描画される必要がある
JavaScriptとスタイルシートを宣言することができます。

PHP:
  1. class MyWidget extends sfWidgetForm
  2.     {
  3.       public function getJavaScripts()
  4.       {
  5.         return array('/path/to/a/file.js');
  6.       }
  7.  
  8.       public function getStylesheets()
  9.       {
  10.         return array('/path/to/a/file.css' => 'all', '/path/to/a/file.css' => 'screen,print');
  11.       }
  12.     }

`getJavaScripts()`メソッドはJavaScriptファイルの配列を返さなければなりません。
`getStylesheets()`メソッドはファイル名をキーとし、メディアを値とする配列を返さなければなりません。

新しい widgets

symfony 1.2 は新しいwidgetが備わっています。

  • `sfWidgetFormDateRange`
  • `sfWidgetFormFilterInput`
  • `sfWidgetFormFilterDate`
  • `sfWidgetFormI18nSelectCurrency`
  • `sfWidgetFormSelectCheckbox`

`sfWidgetFormChoice` ファミリー

新しい`choice` widgetファミリーも追加されました。

  • `sfWidgetFormChoice`
  • `sfWidgetFormChoiceMany`
  • `sfWidgetFormPropelChoice`
  • `sfWidgetFormPropelChoiceMany`

標準で、これらのwidgetsはそれらの`Select`の複製です。しかし、それらは
`sfWidgetFormSelect`、`sfWidgetFormSelect`、`sfWidgetFormSelectRadio`
そして`sfWidgetFormSelectCheckBox`ウィジットの上部にあるラッパーです。
レンダリングのためにこれらの1つを使用します。標準のウィジットを変更するためにはいくつかのオプションを利用できます。

  • `expanded`:
    • もしfalseなら、widgetは`select`タグになります。
    • もしtrueで`multiple`がfalseなら、widgetは`radio`タグのリストになります。
    • もしtrueで`multiple`がtrueなら、widgetは`checkbox`タグのリストになります。
  • `rendered_options`: widget(`select`, `radio`, `checkbox`)を作成するとき、
    これはレンダー処理されるwidgetに渡したいオプションです。

  • `renderer_class`: 標準のクラスに代わりに利用するためのクラス
  • `renderer`: 直接widgetオブジェクトを渡すこともできます。(これは前述のオプションを上書きします)

オプションを利用したサンプルです。

PHP:
  1. ...
  2.     $widget = new sfWidgetFormPropelSelect(array('model' => 'Article'));
  3.  
  4.     // is equivalent to
  5.     $widget = new sfWidgetFormPropelChoice(array('model' => 'Article'));
  6.  
  7.     // change the rendering to use a radio list
  8.     $widget->setOption('expanded', true);
  9.  
  10.     // create a multiple select
  11.     $widget = new sfWidgetFormPropelChoiceMany(array('model' => 'Article'));
  12.  
  13.     // change the rendering to use a checkbox list
  14.     $widget->setOption('expanded', true);

フォームを生成されたPropelのためにこれらの新しいwidgetsは標準で利用できます。

レスポンス(Response)

`sfWebResponse::getCharset()`

`getCharset()`はレンスポンスの現在の文字コードを返します。
コンテントタイプの設定を変更することでこの文字コードは自動的に更新されます。

`sfWebResponse::getStylesheets()` と `sfWebResponse::getJavascripts()`

もし`sfWebResponse::ALL`を最初の引数として渡せば
`getStyleSheets()`と`getJavascripts()`メソッドは現在は全てのスタイルシートとJavaScriptをポジションで指定された順番で返します。

PHP:
  1. ...
  2.     $response = new sfWebResponse(new sfEventDispatcher());
  3.     $response->addStylesheet('foo.css');
  4.     $response->addStylesheet('bar.css', 'first');
  5.  
  6.     var_export($response->getStylesheets());
  7.  
  8.     // outputs
  9.     array(
  10.       'bar.css' => array(),
  11.       'foo.css' => array(),
  12.     )

`sfWebResponse::ALL`はポジションの引数のための標準の値でもあります。

Prototype と Scriptaculous

symfony1.2においてバンドルされていたPrototypeと
Scriptaculousライブラリとヘルパー(`JavascriptHelper`)はコアプラグインに移動されました。
コアプラグインは本物のプラグインのように振る舞いますが、symfonyプラットフォームによって操作されます。

これは本物のプラグインのような新しい`sfProtoculousPlugin`というCSSファイルを作成します。
それらは (1.0や1.1のときに配置されていた)`web/sf`ディレクトリではなく`web/sfProtoculousPlugin`ディレクトリに配置されることになります。
`prototype_web_dir`設定により新しいディレクトリを指定することができるようにもなります。

さらに、色々なJSフレームワークによって利用される基本的なJavascriptヘルパーがコア部分に`JavascriptBaseHelper`として抽出されます。

新しいこととして、`javascript_tag()`は`slot()`のように動作するようになります。次のような使い方ができます。

PHP:
  1. ...
  2.     <?php javascript_tag() ?>
  3.     alert('All is good')
  4.     <?php end_javascript_tag() ?>

テスト(Tests)

`sfBrowser::info()`

多くの機能テストをモジュールのためにかくとき、何が行われているかについて視覚的な情報が役に立つ事があります。
symfony 1.2では `info()`メソッドがあなたのテストをカテゴライズするのに役立つテキストを出力します。

PHP:
  1. ...
  2.     $browser->
  3.       info('First scenario')->
  4.       // ... some tests
  5.       info('Second scenario')->
  6.       // ... some more tests
  7.     ;

デバッキングテスト

機能テストで問題が発生するとき、ブラウザーに送信された情報は原因を診断するために役に立ちます。
symfony 1.2では流暢なインターフェーススタイルを邪魔することなく生成されたHTMLを表示することができます。

PHP:
  1. ...
  2.     $browser->
  3.       get('/a_uri_with_an_error')->
  4.       with('response')->debug()->
  5.       // some tests that won't be executed
  6.     ;

`debug()`メソッドはレスポンスヘッダとコンテンツを出力し、ブラウザのフローを遮ります。

テスター

`sfTestFunctionalBase`クラスは実際のテストを`sfTester`クラスに委任するようになりました。
symfony 1.2はいくつかの組み込みテスタークラスがあります。

  • `request`: `sfTesterRequest`
  • `response`: `sfTesterResponse`
  • `user`: `sfTesterUser`
  • `view_cache`: `sfTesterViewCache`

    全ての古いテストブラウザのメソッドはテスタークラスのメソッドに移動されました。
    class:

    method name tester class new method name
    `isRequestParameter` `sfTesterRequest` `isParameter`
    `isRequestFormat` `sfTesterRequest` `isFormat`
    `isStatusCode` `sfTesterResponse` `isStatusCode`
    `responseContains` `sfTesterResponse` `contains`
    `isResponseHeader` `sfTesterResponse` `isHeader`
    `checkResponseElement` `sfTesterResponse` `checkElement`
    `isUserCulture` `sfTesterUser` `isCulture`
    `isCached` `sfTesterViewCache` `isCached`
    `isUriCached` `sfTesterViewCache` `isUriCached`

    テスタークラスも新しいメソッドが用意されました。

    tester class new method name
    `sfTesterRequest` `hasCookie`
    `sfTesterRequest` `isCookie`
    `sfTesterRequest` `isMethod`
    `sfTesterUser` `isAuthenticated`
    `sfTesterUser` `hasCredential`
    `sfTesterUser` `isAttribute`
    `sfTesterUser` `isFlash`

    以下が新しいテスターの使い方です。

    PHP:
    1. ...
    2.     $browser->
    3.       get('/')->
    4.       with('request')->isParameter('module', 'foo')->
    5.       with('request')->isParameter('module', 'bar')
    6.     ;

    `with()`メソッドを呼び出すことで呼び出し可能なメソッドにあるテスターオブジェクトを返します。

    もし、複数のメソッドをテスターオブジェクトで呼び出したいなら、それらを`block`内におくことができます。

    PHP:
    1. ...
    2.     $browser->
    3.       get('/')->
    4.  
    5.       with('request')->begin()->
    6.         isParameter('module', 'foo')->
    7.         isParameter('module', 'bar')->
    8.         isMethod('get')->
    9.         isFormat('html')->
    10.       end()->
    11.  
    12.       with('response')->begin()->
    13.         isStatusCode(200)->
    14.         checkElement('body', 'foo')->
    15.         isHeader('Content-Type', 'text/html; charset: UTF-8')->
    16.         isRedirected(false)->
    17.       end()
    18.     ;

    `sfTester`から継承されたクラスを作成し登録することによって独自のテスターを追加することができます。

    PHP:
    1. $browser->setTester('my_tester', 'myTesterClassName');

    既存のテスタークラスも標準のクラス名を上書きすることで拡張することができます。

    PHP:
    1. $browser->setTester('request', 'myTesterRequest');

    formがあるページのテストを改善するために、`select()`と`deselect()`メソッドをテストブラウザに追加しました。
    現在はチェックボックスの選択/非選択とラジオボタンの選択処理をサポートしており、これは同じ`name`のラジオボタンが非選択になる原因かもしれません。

    PHP:
    1. $browser->
    2.       select('aRadioId')->
    3.       select('aCheckboxId')->
    4.       deselect('anotherCheckbox');

    Cookies

    クッキーは `sfBrowser`や`sfTestBrowser`クラスにおいてもサポートされるようになります。

    クッキーを `sfBrowser`クラスにある`setCookie()`、`removeCokie()`そして`clearCookies()`メソッド間で扱うことができます。

    PHP:
    1. ...
    2.     $b->
    3.       setCookie('foo', 'bar')->
    4.       removeCookie('bar')->
    5.       get('/')->
    6.       // ...
    7.  
    8.       clearCookies()->
    9.       get('/')->
    10.       // ...

    また、`sfTesterRequest`クラスから`hasCookie()`と`isCookie()`メソッドでクッキーをテストすることができます。

    PHP:
    1. ...
    2.     $b->
    3.       get('/')->
    4.       with('request')->begin()->
    5.         hasCookie('foo')->
    6.         hasCookie('foobar', false)->
    7.         isCookie('foo', 'bar')->
    8.         isCookie('foo', '/a/')->
    9.         isCookie('foo', '/!z/')->
    10.       end()
    11.     ;

    `hasCookie()`メソッドは第2引数でクッキーがセットされてない状態をテストするための真偽値をとります。

    `isCookie()`メソッドの第2引数は`checkElementResponse`メソッドの第2引数のような振る舞いをします。
    それは文字列、正規表現もしくは否定正規表現だったりします。

    ブラウザークラスは自動的に各クッキーに`expire`値ごとにクッキーを消します。

    リクエスト(Request)

    リクエストテスターから`isMethod()`メソッドを使ってあなたの機能的なテストでの
    リクエストで使用されているHTTPメソッドをテストすることもできます。

    PHP:
    1. ...
    2.     $b->
    3.       setField('login', 'johndoe')->
    4.       click('Submit')->
    5.       with('request')->isMethod('post');

    リンク(Links)

    リンクになっているクリックやボタンをシミュレーションするとき、`click()`メソッドという名前を使います。
    しかし、同じ名前で2つの異なるリンクやボタンを差別化するほうほうがありません。

    symfony 1.2 では `click()`メソッドではオプションを第3引数に渡します。

    `position`というオプションをクリッックしたいしたいリンクを変更するときに渡します。

    PHP:
    1. ...
    2.     $b->
    3.       click('/', array(), array('position' => 1))->
    4.       // ...
    5.     ;

    標準で、symfonyはページにある項目の最初のリンクをクリックします。

    `method`オプションでクリックしたいリンクやフォームのメソッドを変更することができます。

    PHP:
    1. ...
    2.     $b->
    3.       click('/delete', array(), array('method' => 'delete'))->
    4.       // ...
    5.     ;

    これはリンクがJavascriptで動的に生成されるフォームで変換される場合に役立ちます。

    フォーム(Forms)

    新しいフォームフレームワークを使うなら、サブミットされたフォームから生成されたエラーをテストすることができるようになりました。

    PHP:
    1. ...
    2.     $browser->
    3.       click('save', array(...))->
    4.       with('form')->begin()->
    5.         hasErrors()->
    6.         isError('name', 'Required.')->
    7.         isError('name', '/Required/')->
    8.         isError('name', '!/Invalid/')->
    9.         isError('name')->
    10.         isError('name', 1)->
    11.       end()
    12.     ;

    また、`debug()`メソッドでフォームをデバッグすることもできます。

    PHP:
    1. ...
    2.     $browser->
    3.       click('save', array(...))->
    4.       with('form')->debug()->
    5.       // some tests that won't be executed
    6.     ;

    もしエラーがあればサブミットされた値と全てのエラーを出力します。

    Propel

    Propelプラグインにはpropelテスターが附属しています。このテスターは標準では登録されていません。

    PHP:
    1. $browser->setTester('propel', 'sfTesterPropel');

    このテスターはPropelモデルをテストするための`check()`メソッドを提供します。

    PHP:
    1. ...
    2.     $browser->
    3.       post('...', array(...))->
    4.       with('propel')->begin()->
    5.         check('Article', array(), 3)->
    6.         check('Article', array('title' => 'new title'))->
    7.         check('Article', array('title' => 'new title'))->
    8.         check('Article', array('title' => 'new%'))->
    9.         check('Article', array('title' => '!new%'), 2)->
    10.         check('Article', array('title' => 'title'), false)->
    11.         check('Article', array('title' => '!title'))->
    12.       end()
    13.     ;

    モデル名を最初の引数としてとり、第2引数として`Criteria`オブジェクトか条件の配列をとります。
    そして第3引数は次のどれかでなければなりません。

    * `true`: `Criteria`が返す最低でも1つのオブジェクトをチェックする
    * `false`: `Criteria`がオブジェクトを返さないことをチェックする
    * 返されるオブジェクトの数をチェックするための数値

    カバレッジ(Coverage)

    実行したテストのコードカバレッジを出力するための新しい`test:coverage`タスクが用意されました。

    $ ./symfony test:coverage test/unit/model/ArticleTest.php lib/model/Article.php

    最初の引数はファイルかテストのディレクトリです。第2引数はあなたがカバーしたいファイルかディレクトリになります。

    もし、どの行がカバーされていないかを知りたいなら、次のように`--detailed`オプションを追加するだけです。

    ./symfony test:coverage --detailed test/unit/model/ArticleTest.php lib/model/Article.php

    ロガー(Loggers)

    symfony 1.2 は新しいロガーを組み込むことになりました。それが`sfVarLogger`です。
    このロガークラスは後で利用するために全てのメッセージを配列でロギングします。
    ログをとって、それらを次のように使うことができるようになります。

    PHP:
    1. ...
    2.     $log = new sfVarLogger();
    3.     $log->log('foo');
    4.  
    5.     var_export($log->getLogs());
    6.  
    7.     // outputs
    8.     array(
    9.       0 => array(
    10.         'priority'      => 6,
    11.         'priority_name' => 'info',
    12.         'time'          => 1219385295,
    13.         'message'       => 'foo',
    14.         'type'          => 'sfOther',
    15.         'debug_stack'   => array()
    16.       )
    17.     )

    各ログは`priority`、`priority_name`、`time`、`message`、`type`そして`debug_stack`というキーを持つ
    連想配列です。

    `debug_stack`属性は`xdebug_logging`オプションが`true`にセットされているときだけセットされます。
    オプションがセットされているときはxdebugのスタックトレースを配列をして保持しています。

    `sfVarLogger`はまた`sfWebDebugLogger`クラスのための基礎クラスでもあります。
    そのため、`sfWebDebugLogger`の`xdebugLogging`オプションは`xdebug_logging`に名前が変更されました。

    Web Debug Toolbar

    ウェブデバッグツールバーはカスタマイズができるようになりました。ツールバーはパネルで構成されています。
    パネルは`sfWebDebugPanel`を拡張したオブジェクトであり、ツールバー上に表示するために必要とされる情報を
    提供してくれます。

    標準では、次のようなパネルが自動的に登録されています。

    名前 クラス名
    symfony_version `sfWebDebugPanelSymfonyVersion`
    cache `sfWebDebugPanelCache`
    config `sfWebDebugPanelConfig`
    logs `sfWebDebugPanelLogs`
    time `sfWebDebugPanelTimer`
    memory `sfWebDebugPanelMemory`
    db `sfWebDebugPanelPropel`

    ウェブデバッグツールバーを`debug.web.load_panels`イベントに聞く事でカスタマイズできます。
    このリスナーで追加された新しいパネルを追加したとき、既に存在するパネルを削除したときや
    別のものに置き換えることができます。

    `sfWebDebugPanelLogs`パネルは`debug.web.filter_logs`イベントを通知することで表示されるログをフィルター処理します。
    たとえば、`sfWebDebugPanelPropel`と`sfWebDebugPanelTimer`は全てのPropelに関連するログやログパネルから
    タイマーログを取り除くためにこのイベントに接続します。

    ウェブデバッグツールバーに表示される全体の時間は`$_SERVER['REQUEST_TIME']`(以前は`sfConfig::get('sf_time_start')`
    で計算していましたが、もやはこれは存在しません)で計算しています。
    表示される時間はsymfony1.0や1.1よりもより長くなっていることを意味します。

    タスク(Tasks)

    取り込まれているPhingタスクが失敗するなら、Phingに依存しているPropelタスクははっきりとした
    エラーメッセージを出力します。

    CLIタスクの中にはアプリケーション名を引数として必要としているものがあります。なぜなら
    それらはデータベースに接続する必要があるからです。アプリケーション上でカスタマイズされる設定ファイルが必要なため
    私たちはアプリケーションが必要なのです。そして全てのsymfonyの設定システムはアプリケーションレベルが基礎となっています。

    これらのタスクにとって、引数はオプションである"application"オプションになって取り除かれました。
    もし、"application"オプションを提供しないならsymfonyはプロジェクトの`databases.yml`ファイルから
    データベース設定を取得してくるようになりました。

    次のタスクの用法はこれらに従って変更されました。

    • `propel:build-all-load`
    • `propel:data-dump`
    • `propel:data-load`

    ノート

    これは`sfDatabaseManager`が今ではプロジェクト設定やアプリケーション設定を取得するようになったので可能になりました。

    興味深いこととで、`sfDatabaseConfigHandler`は今では静的か動的な設定ファイルの配列に基づく設定を返すから動作するのです。
    (`exeute()`か`evaluate()`を見てください)

    デバッグのために、`propel;build-model`, `propel:build-all`そして
    `propel:build-all-load`タスクは `--trace`オプションを指定すれば生成されるスキーマを取り除きません。

    `propel:insert-sql`タスクは全てのデータをデータベースから取り除きます。
    情報を破壊するので、タスクの実行前にユーザーに訪ねるようになりました。
    同じことが`propel:build-all`と`propel:build-all-load`タスクにもあてはまります。

    もし、これらのタスクをバッチ処理で使いたいなら確認のための質問を避けたいでしょう。そのときは
    `no-confirmation`オプションを渡してください。

    $ php symfony propel:insert-sql --no-confirmation

    symfony 1.2より前では、`propel:insert-sql`タスクは`propel.ini`からのデータベースの情報を読み取ることしかしませんでした。
    symfony 1.2では`databases.yml`から読みとるようになりました。そのため、モデルに複数の異なる接続先を設定したいなら
    このタスクはそれらを考慮するようになりました。
    この新しい機能のおかげで、 もし指定した接続先からのSQLだけを読み込みたい場合に
    `--connection` オプションを使う事ができるようになりました。

    $ php symfony propel:insert-sql --connection=propel

    また`--env`と`--application`オプションも次のように特定の設定を選択するために使う事ができます。

    $ php symfony propel:insert-sql --env=prod --application=frontend

    タスクのために利用可能な新しいメソッド

    `sfTask`基底クラスはタスクで利用できる3つの新しいメソッドを提供するようになりました。

    • `logBlock()`: スタイル化されたブロックでメッセージをログします。(標準のスタイルはINFO, COMMENT, ERRORです)
    • `ask()`: ユーザーに質問を行い回答を返します。
    • `askConfirmation()`: 確認をユーザーに行い、ユーザーが確認すればtrueをそうでなければfalseを返します。

    `propel:generate-module`

    `propel:generate-crut`は`propel:generate-module`に名前が変更されました。
    古いタスク名はエイリアスとしてまだ利用することができます。

    `propel:generate-module`タスクの`non-atomic-actions`オプションは取り除かれ次のような新しいオプションが追加されました。

    • singular: アクションとテンプレートのための単数形名
    • plural: アクションとテンプレートのための複数形名
    • route-prefix:  使用するルーティング接頭辞
    • with-propel-route: Whether the related routes are Propel aware

    `propel:generate-module-for-route`

    新しい`propel:generate-module-for-route`タスクはルーティングの定義に基づいたモジュールを作成します。

    php symfony propel:generate-module-for-route frontend articles

    `app:routes`

    いまではsymfonyは(以下のような)ルーティングを自動生成でき、`app:routes`タスクは
    現在のアプリケーションに設定されているルーティングのリストを表示します。

    php symfony app:routes frontend

    もしルーティングについて詳しく知りたいなら、次のようにルート名を追加するだけです。

    php symfony app:routes frontend articles_update

    `plugin:install-assets`

    通常プラグインはタスクでインストールされるとき正しくコアプラグインが必要とするように
    webディレクトリにシンボリックリンクが作成されます。
    これと同じことを手動で行うためには以下のようにします

    `symfony plugin:publish-assets --only-core`

    目標はプロジェクトの生成とアップグレードタスクがあなたに代わってこれを行うことです。

    ルーティング(Routing)

    Routing オプション

    ルーティングタスクは2つのオプションをとります。

    • `generate_shortest_url`: 最も短いURLを生成することができるかどうか
    • `extra_parameters_as_query_string`: クエリストリングとして外部パラメータを生成するかどうか

    標準では、symfony 1.0, 1.1と互換性を保つために`factories.yml`において`false`がセットされています。

    ルーティングの基礎部分でこれらのオプションの有効無効を設定することもできます。

    CSS:
    1. ...
    2.     articles:
    3.       url:     /articles/:page
    4.       param:   { module: article, action: list, page: 1 }
    5.       options: { generate_shortest_url: true }

    できるだけ最も短いURLを生成しようとするので、もし、`1`の`page`の値を渡すなら、
    生成されるURLは`/articles`になるでしょう

    PHP:
    1. ...
    2.     echo url_for('@articles?page=1');
    3.     // /articles
    4.     // would have been /articles/1 in symfony 1.1
    5.  
    6.     echo url_for('@articles?page=2');
    7.     // /articles/2

    `extra_parameters_as_query_string`の例は以下のようになります。

    CSS:
    1. ...
    2.     articles:
    3.       url:     /articles
    4.       options: { extra_parameters_as_query_string: true }

    ルーティングは外部パラメータを受け取り、クエリストリングとしてそれらを追加します。

    PHP:
    1. ...
    2.     echo url_for('@articles?page=1');
    3.     // /articles?page=1
    4.     // would not have matched the route in symfony 1.1
    5.  
    6.     echo url_for('@articles?page=2');
    7.     // /articles?page=2

    `sfRoute`

    `sfPatternRouting`クラスはフラットな連想配列の代わりに`sfRoute`オブジェクトの配列で
    ルーティングをためています。

    標準では、symfonyは組み込みの`sfRoute`オブジェクトを使います。しかし次のように特定の`class`エンティティを
    `routing.yml`設定ファイルで変更することができます。

    CSS:
    1. ...
    2.     foo_bar:
    3.       url:   /foo/:bar
    4.       class: myRoute
    5.       param: { module: foo, action: bar }

    全てのルーティングのロジックは`sfRoute`クラスに含まれています。これは解析と
    あなた自身で生成ロジックを上書きすることができるということです。

    TIP

    より簡単に標準の挙動をカスタマイズできるように`sfRoute`クラスは古い`sfPatternRougint`クラスよりさらにモジュール化されました。
    "compilation"フェーズはより小さなメソッドにリファクタリングされ、コードはよりシンプルになり、"本当の"トークナイザーに基づいています。

    PHPコードであなたのルーティングに接続するなら、`sfRoute`インスタンスを`connect()`、`preprendRoute()`
    、`appendRoute()`そして`insertRouteBefore()`メソッドの第2引数として渡さなければなりません。

    PHP:
    1. ...
    2.     $route = new myRoute('/foo/:bar', array('module' => 'foo', 'action' => 'bar'));
    3.     $routing->connect('foo_bar', $route);

    リクエストがあったばあい、一致しているルーティングオブジェクトはリクエストの属性として保存され、
    `getRoute()`メソッドを通してアクションから取得することができます。

    PHP:
    1. ...
    2.     public function executeIndex()
    3.     {
    4.       $route = $this->getRoute();
    5.     }

    `sfRoute`コンストラクターは各ルーティングをカスタマイズできるようにその最後の引数としてオプションの配列をとります。
    `routing.yml`設定ファイルで、`options`キーを次のようにつかいます。

    CSS:
    1. article:
    2.       url:     /article/:id-:slug
    3.       options: { segment_separators: [/, ., -] }

    `sfRequestRoute`

    symfonyは別の組み込みルーティングである `sfRequestRoute`を持っており、これによって
    HTTPメソッドをあなたのルーティングに強制することができます。

    CSS:
    1. ...
    2.     article:
    3.       url:          /article/:id
    4.       requirements: { sf_method: get }
    5.       class:        sfRequestRoute

    もし、`sf_method`を必要とせず指定しなkれば、symfonyは`get`か`head`リクエストを強制します。

    `sfRouting`の`parse()`メソッドが第2引数としてコンテキストをとるため可能になっています。
    リクエストがルーティングを呼び出すとき、次のようなコンテキストを渡します。

    `method`:
    HTTPメソッド名
    `format`:
    リクエストのフォーマット
    `host`:
    ホスト名
    `is_secure`:
    リクエストがHTTPSで呼び出されたかどうか
    `request_uri`:
    完全なリクエストURI
    `prefix`:
    生成されたルーティングに付け加えられる接頭辞

    前述のルーティング設定で、`article`へのルーティングはHTTPメソッドが`get`の場合のみ一致します。
    同じ`url`を異なるメソッドを必要とするようにルーティングを設定するなら、次のようにルーティングを生成するとき
    `sf_method`パラメータを渡すことができます。

    PHP:
    1. <?php echo link_to('Great article', '@article?id=1&sf_method=get')) ?>

    `sfObjectRoute`

    symfony 1.2 は`sfRequestRoute`を拡張した別のルーティングクラスである `sfObjectRoute`を実装しています。

    `sfObjectRoute`はPHPオブジェクトにルーティングを結びつけています。
    `sfObjectRoute`はオブジェクトやオブジェクトの集まりやルーティングに関連するコレクションを
    取得するためのPHPのクラスにメソッドを呼び出せるようにしたりします。

    CSS:
    1. ...
    2.     article:
    3.       url:     /article/:id
    4.       class:   sfObjectRoute
    5.       options: { model: Article, type: object, method: getById }

    URLがルーティングに一致するとき、`sfObjectRoute::getObject()`メソッドで、
    `Article::getById()`メソッドによって呼ばれる関連するオブジェクトを取得することができます。

    同じことがオブジェクトの集まりにも当てはまります。

    CSS:
    1. ...
    2.     articles:
    3.       url:     /articles/newest
    4.       class:   sfObjectRoute
    5.       options: { model: Article, type: list, method: getNewest }

    `sfPropelRoute`

    `sfPropelRoute`は`sfObjectRoute`をPropelのモデルへのルーティングへ結びつけるために拡張します。

    アクションにおいて、`sfObjectRoute::getObject()`や`sfObjectRoute::getObjects()`メソッドを利用することで関連するオブジェクトやオブジェクトの集まりを
    取得することができます。

    PHP:
    1. ...
    2.     public function executeList($request)
    3.     {
    4.       $this->articles = $this->getRoute()->getObjects();
    5.     }
    6.  
    7.     public function executeShow($request)
    8.     {
    9.       $this->article = $this->getRoute()->getObject();
    10.     }

    ここに`Article`Propelオブジェクトのための例があります。

    CSS:
    1. ...
    2.     article:
    3.       url:     /article/:id
    4.       param:   { module: article, action: show }
    5.       class:   sfPropelRoute
    6.       options: { model: Article, type: object }
    7.  
    8.     articles:
    9.       url:     /articles
    10.       param:   { module: article, action: list }
    11.       class:   sfPropelRoute
    12.       options: { model: Article, type: list, method: getPublishedArticleCriteria }

    メソッドを定義していないなら、`sfPropelRoute`は利用できるルーティングの変数に基づいて`Criteria`オブジェクトを構築することで
    オブジェクトを取得します。

    `sfPropelRoute`は`sfRoute`以上の2つの主な利点があります。

    • リクエストがありルーティングがURLに一致するとき、一致する`sfPropelRoute`オブジェクトは
      アクションにて利用することができ、関連する`Article`オブジェクトは`getObject()`メソッドを呼ぶ事で
      利用することができます。
      さらに、オブジェクトがデータベースに存在しないなら、自動的に404エラーページにリダイレクトします。
      これはアクションでの決まりきった指示を減らすことができるということです。

    • このルーティングへのリンクを作成するとき、新しい`url_for()`を使うことができ、
      次のようにArticleオブジェクトに直接引数を渡すことができます。
    PHP:
    1. <?php echo url_for('article', $article) ?>

    その他のパラメータを渡すなら(例えばHTTPメソッドを強制したいような場合)、次のように配列を使うことができます。

    PHP:
    1. <?php echo url_for('article', array('sf_subject' => $article, 'sf_method' => 'get')) ?>

    もちろん、次のように完全なパラメータもまだ利用することができます。

    PHP:
    1. <?php echo url_for('article', array('id' => $article->getId(), 'slug' => $article->getSlug())) ?>

    もしくは次のように内部URLを使うこともできます。

    PHP:
    1. <?php echo url_for('@article?id='.$article->getId().'&slug='.$article->getSlug()) ?>

    `sfPropelRoute`はArticleオブジェクトを自動的にパラメータの配列に変換します。

    `sfPropelRoute`はプライマリキーだけでは動作しません。色々なカラムを用いて動作させることができます。
    次の例では、`slug`カラムをルーティングパターンに追加することができます。

    CSS:
    1. ...
    2.     article:
    3.       url:     /article/:id/:slug
    4.       param:   { module: article, action: show }
    5.       class:   sfPropelRoute
    6.       options: { model: Article, type: object }

    しかし時には、データベースに存在しない情報をパターンに含めたい場合もあるでしょう。
    このような場合、`method`オプションに渡すことができます。

    CSS:
    1. ...
    2.     post:
    3.       url:     /post/:id/:year/:month/:day/:slug
    4.       param:   { module: article, action: show }
    5.       class:   sfPropelRoute
    6.       options: { model: Article, type: object, method: getObjectForRoute }

    `getObjectForRoute()`は最初の引数としてパラメータの配列を受け取り`Article`オブジェクトを返さなければなりません。

    PHP:
    1. ...
    2.     class ArticlePeer extends BaseArticlePeer
    3.     {
    4.       static public function getObjectForRoute($parameters)
    5.       {
    6.         $criteria = new Criteria();
    7.         $criteria->add(self::ID, $parameters['id']);
    8.  
    9.         return self::doSelectOne($criteria);
    10.       }
    11.     }

    `sfPropelRoute`はパラメータを解析されたルートを上書きするための`setListCriteria()`メソッドも提供します。
    ルーティングによって返されるオブジェクトを洗練したいときに特に役立ちます。
    たとえば、他のパラメータに基づくような次のような検索などです。

    PHP:
    1. ...
    2.     public function executeList($request)
    3.     {
    4.       $this->filters = new ArticleFormFilter();
    5.       if ($request->isMethod('post'))
    6.       {
    7.         $this->filters->bind($request->getParameter('article_filters'));
    8.         if ($this->filters->isValid())
    9.         {
    10.           $this->getRoute()->setListCriteria($this->filters->getCriteria());
    11.         }
    12.       }
    13.  
    14.       $this->articles = $this->getRoute()->getObjects();
    15.     }

    `sfRouteCollection`, `sfObjectRouteCollection`, and `sfPropelRouteCollection`

    symfonyは"collection"ルートクラスも実装しています。

    これらのクラス`sfRouteCollection`, `sfObjectRouteCollection`, `sfPropelRouteCollection`)は定義に基づいた標準的なルーティングを作成します。

    CSS:
    1. ...
    2.     articles:
    3.       class:   sfPropelRouteCollection
    4.       options: { model: Article, module: article }

    標準では、このルーティングは以下の7つのルーティングを生成します。

    list:
    `articles GET /articles.:sf_format`
    new:
    `articles_new GET /articles/new.:sf_format`
    create:
    `articles_create POST /articles.:sf_format`
    edit:
    `articles_edit GET /articles/:id/edit.:sf_format`
    update:
    `articles_update PUT /articles/:id.:sf_format`
    delete:
    `articles_delete DELETE /articles/:id.:sf_format`

    複数のオプションで生成されるルーティングをカスタマイズすることができます。

    model:
    モデルクラス名を指定
    actions:
    (上記の7個のリストから)生成されるアクションのリストを指定
    module:
    モジュール名
    prefix_path:
    各ルーティングのために追加する接頭辞
    column:
    プライマリキーとするカラム名(標準では`id`)
    with_show:
    showメソッドを追加するかどうか
    segment_names:
    `new`と`edit`のためのセグメント名
    model_methods:
    オブジェクトのコレクションを取得するために使うメソッド
    requirements:
    標準の必須条件(標準では`id`は`\d+`を要求します)
    route_class:
    利用するためのルートクラス

    (標準では `sfObjectRoute`のためには`sfObjectRouteCollection`
    `sfPropelRouteCollection`のためには`sfPropelRoute`を利用)

    collection_actions:
    コレクションアクションとして追加するためのアクションのリスト
    object_actions:
    オブジェクトアクションとして追加するためのアクションのリスト

    ここにオプションを指定した場合の別の例があります。

    CSS:
    1. ...
    2.     articles:
    3.       class:   sfPropelRouteCollection
    4.       options:
    5.         model:              Article
    6.         module:             article
    7.         prefix_path:        /foo
    8.         methods:            [ list, edit, update ]
    9.         segment_names:      { list: nouveau, edit: edition }
    10.         collection_actions: { filter: post }
    11.         object_actions:     { publish: put }

    URLヘルパー(URL helpers)

    `link_to()` and `url_for()`

    もし、`POST`, `PUT`または`DELETE`HTTPメソッドでサブミットされなければならない
    リソースへのリンクを作成したいとき、`link_to()`ヘルパーで`method`オプションを渡すことで
    リンクをフォームに変換することができます。

    PHP:
    1. <?php echo link_to('@article_delete?id=1', array('method' => 'delete')) ?>

    古い`post`オプションはまだ有効ですが廃止される予定です。

    PHP:
    1. ...
    2.     // 次は廃止される予定です
    3.     <?php echo link_to('@some_route', array('post' => true)) ?>
    4.  
    5.     // 次で同じ実装になります
    6.     <?php echo link_to('@some_route', array('method' => 'post')) ?>

    `ulf_for()`と`link_to()`ヘルパーは新しい特徴をサポートするようになりました。
    内部URIの代わりに、ルーティング名とパラメータの配列もとることができます。

    PHP:
    1. ...
    2.     echo url_for('@article', array('id' => 1));
    3.     echo link_to('Link to article', '@article', array('id' => 1));

    何も変更を加えなくてもこれまでの設定は動作します。

    設定(Configuration)

    symfony 1.2より前は、全てのプラグインは`plugins`ディレクトリにインストールされていました。
    そして、全ての組み込みプラグインは自動的に読み込まれていました。

    symfony 1.2では、あなたのプロジェクト内で利用したいプラグインを有効にする必要があります。
    `ProjectConfiguration`クラスでこれを行うことができます。
    以下がDoctrineプラグインを有効にし、Propelプラグインを無効にする方法です。

    PHP:
    1. ...
    2.     public function setup()
    3.     {
    4.       $this->enablePlugins('sfDoctrinePlugin');
    5.       $this->disablePlugins('sfPropelPlugin');
    6.     }

    複数のプラグインを一度に追加する倍はプラグイン名の配列を渡します。

    PHP:
    1. ...
    2.     public function setup()
    3.     {
    4.       $this->enablePlugins(array('sfDoctrinePlugin', 'sfGuardPlugin'));
    5.     }

    `setPlugins`メソッドを用いることでプラグインの読み込まれる順番を変更することができます。

    PHP:
    1. ...
    2.   public function setup()
    3.   {
    4.     $this->setPlugins(array('sfDoctrinePlugin', 'sfCompat10Plugin'));
    5.   }

    `settings.yml`にあった`orm`設定は自動的にORMプラグインが読み込まれるときにセットされるようになったので廃止される予定です。

    `settings.yml`設定にある`compat_10`設定も廃止される予定です。これは`sfCompat10Plugin`が読み込まれたときに
    自動的にセットされるようになったからです。

    そのため、1.0と互換性のあるプラグインを有効にするために、設定で次のように有効にする必要があります。

    PHP:
    1. ...
    2.     public function setup()
    3.     {
    4.       $this->enablePlugins('sfCompat10Plugin');
    5.     }

    標準では、symfonyはPropelプラグインだけが有効です。

    全てのインストールしたプラグインも有効にすることができます。

    PHP:
    1. ...
    2.     public function setup()
    3.     {
    4.       $this->enableAllPluginsExcept('sfDoctrinePlugin');
    5.     }

    上のようにしてDoctrineプラグイン以外の全てのプラグインを有効にすることができます。
    1.0または.1.1からアップグレードする場合は、この行によってsymfony 1.0と1.1のときのようにsymfonyを動作させることができます。

    Plugin configuration

    プラグインはプラグインを設定するクラスのためオプションが存在します。
    これらのプラグイン設定クラスはプラグインのためにオートローディングを設定し、`sfProjectConfiguration`にインストールされています。
    symfony1.2タスクはプラグインクラスが仕様するアプリケーション名をもはや必要としません。
    プラグインは`command.*`イベントに接続することで以前はできなかったことができるようになりました。

    フィルター(Filters)

    symfonyはあなたのモデルに基づくフィルター(検索フォーム)を`propel:build-filters`タスクで生成することができるようになりました。

    $ php symfony propel:build-filters

    このタスクは標準では`lib/filter/`ディレクトリにフィルタ(検索フォーム)クラスを生成します。

    `Article`モデルをフィルター処理するためには、以下のようにアクションでコードを書く必要があります。

    PHP:
    1. ...
    2.     $this->filters = new ArticleFormFilter();
    3.     if ($request->isMethod('post'))
    4.     {
    5.       $this->filters->bind($request->getParameter('article_filters'));
    6.       if ($this->filters->isValid())
    7.       {
    8.         $this->articles = ArticlePeer::doSelect($this->filters->getCriteria());
    9.       }
    10.     }

    そして、以下が関連するテンプレートのコードです。

    PHP:
    1. ...
    2.     <form action="<?php echo url_for('@articles_filter') ?>" method="post">
    3.       <table>
    4.         <?php echo $filters ?>
    5.         <tr>
    6.           <td colspan="2">
    7.             &nbsp;<a href="<?php echo url_for('@articles') ?>">Reset</a>
    8.             <input type="submit" value="Filter" />
    9.           </td>
    10.         </tr>
    11.       </table>
    12.     </form>

    もし、永続的にリクエスト間でフィルターを保持したいなら、以下のようなちょっとした動作例のように行います。

    PHP:
    1. ...
    2.     public function executeList($request)
    3.     {
    4.       $this->filters = new ArticleFormFilter($this->getUser()->getAttribute('article_filters'));
    5.       if ($request->isMethod('post'))
    6.       {
    7.         $this->filters->bind($request->getParameter('article_filters'));
    8.         if ($this->filters->isValid())
    9.         {
    10.           $this->getUser()->setAttribute('article_filters', $this->filters->getValues());
    11.         }
    12.       }
    13.  
    14.       $criteria = $this->filters->buildCriteria($this->getUser()->getAttribute('article_filters'));
    15.       $this->articles = ArticlePeer::doSelect($criteria);
    16.     }

    テンプレートの例外処理(Exception Templates)

    キャッチできないあらゆる発生した例外をレンダー処理するとき、symfonyは現在のリクエストフォーマットを考慮するようになりました。
    プロジェクトまたはアプリケーションの`config/error`ディレクトリにテンプレートを追加することで各フォーマットの出力をカスタマイズできます。

    例えば、XMLリクエストで発生したキャッチできない例外はアプリケーションがデバッグモード時は`config/error/exception.xml.php`を、
    デバッグモードでない場合は`config/error/error.xml.php`をレンダー処理します。

    プロジェクトディレクトリ内にカスタマイズした500エラーテンプレートを用意していれば、
    それを新しいディレクトリに手動で移動する必要があります。

    • symfony 1.1 では `config/error500.php` から `config/error/error.html.php`に
    • symfony 1.0 では `web/errors/error500.php` から `config/error/error/html/php`に

    より小さい改善(Smaller improvements)

    symfony 1.2は多くの改善があちこちでなされています。ここにそのいくつかを紹介します。

    Action

    アクションでは、`generateUrl()`を呼ぶことで直接ルーティングオブジェクトを使うことでURLを生成することができるようになります。

    PHP:
    1. ...
    2.     public function executeIndex()
    3.     {
    4.       $this->redirect($this->generateUrl('homepage'));
    5.     }

    `generateUrl()`メソッドにはルーティング名、パラメータの配列、絶対的URLを生成するかどうかをコンストラクターの引数として渡すことができます。

    標準で、`redirectIf()`や``redirectUnless()'メソッドをアクションの中でつかうとき、
    symfonyはHTTPレスポンスのステータスを自動的に302に変更します。

    これらの2つのメソッドは追加のおpションを持つようになり、この標準のステータスコードを変更できるようになりました。

    PHP:
    1. ...
    2.     $this->redirectIf($condition, '@homepage', 301);
    3.     $this->redirectUnless($condition, '@homepage', 301);

    `redirect()`メソッドにおいては既にこの機能は実装されています。

    YAML

    YAMLパーサーは完全なキーのマージをサポートしました。(サンプルについてはhttp://yaml.org/type/merge.htmlをみてください)

    PHP:
    1. ...
    2.     $yaml = new sfYamlParser();
    3.  
    4.     var_export($yaml->parse(<<<EOF
    5.     default_param: &default_param
    6.       datasource: propel
    7.       phptype:    mysql
    8.       hostspec:   localhost
    9.       database:   db
    10.       username:   user
    11.       password:   pass
    12.  
    13.     param:
    14.       <<: *default_param
    15.       username:   myuser
    16.       password:   mypass
    17.     EOF
    18.     ));
    19.  
    20.     // outputs
    21.     array(
    22.       'default_param' => array(
    23.         'datasource' => 'propel',
    24.         'phptype'    => 'mysql',
    25.         'hostspec'   => 'localhost',
    26.         'database'   => 'db',
    27.         'username'   => 'user',
    28.         'password'   => 'pass',
    29.       )
    30.  
    31.       'param' => array(
    32.         'datasource' => 'propel',
    33.         'phptype'    => 'mysql',
    34.         'hostspec'   => 'localhost',
    35.         'database'   => 'db',
    36.         'username'   => 'myuser',
    37.         'password'   => 'mypass',
    38.       )
    39.     )

    sfParameterHolder

    The `sfParameterHolder`の`has()`メソッドはよりその本来の正しい動作になるように変更されました。

    もし値が`null`だった場合は`true`を返すようになりました。

    PHP:
    1. ...
    2.     $ph = new sfParameterHolder();
    3.     $ph->set('foo', 'bar');
    4.     $ph->set('bar', null);
    5.  
    6.     $ph->has('foo') === true;
    7.     $ph->has('bar') === true; // returns false under symfony 1.0 or 1.1

    `sfparameterHolder::has()`メソッドは多くのコアクラスの中で`hasParameter()`や
    `hasAttribute()`メソッドで利用されています。

    `image_tag()` helper

    symfony 1.0と1.1では `image_tag`ヘルパーはファイル名からimgタグの`alt`属性を生成しています。
    これからは`sf_compat_10`が有効になっているときにだけこのように動作します。
    これからは(x)htmlバリデーターが使っているセットされていないalt属性を簡単に見つけることができます。
    さらに、新しい`alt_title`オプションによってaltをセットし、タイトル属性を同じ値にすることができます。
    これはクロスブラウザーでのツールチップとして利用できます。

    View Configuration

    symfony 1.2では、`module.yml`で`partial_view_class`を設定することでパーシャルを処理するために
    使用されるビュークラスを変更することができるよになりました。
    このクラスは`sfPartialView`を拡張したものでなければなりません。
    同じことが`view_class`設定でも行えます。symfonyは自動的に`PartialView`を
    `partial_view_class`の値に適用しようとします。

    CSS:
    1. ...
    2.     # module.yml
    3.     all:
    4.       partial_view_class: my

    Factories

    `sfViewCacheManager` クラスは `factories.yml`で設定できるようになりました。

    CSS:
    1. ...
    2.     view_cache_manager:
    3.       class: sfViewCacheManager

    View

    標準の`sf_cache_namespace_callable`設定で`sfViewCacheManager::generateCacheKey()`
    を上書きすることができます。
    symfony1.2では、collableは追加の引数のビュークラスインスタンスで呼び出されるようになりました。

    Events

    symfony 1.2には新しいイベント機能が実装されています。

    `user.change_authentication`:
    ユーザーの認証ステータスが変更されたときに通知します。
    このイベントは変更が発生したあとに引数として`authenticated`フラグをとることができます。
    `debug.web.load_panels` (例. ウェブデバッグツールバーパラグラフ)
    `debug.web.filter_logs` (例. ウェブデバッグツールバーパラグラフ)

    I18N

    内部的に、`sfCulterUnfo`クラスはこれらかはシングルトンとしてのみ利用できるようになります。
    `getInstance()`メソッドをバイパスすることが可能で、新しいオブジェクトを直接インスタンス化することができるとしても
    廃止される予定です。
    シングルトンとして利用するために、1リクエストの度に1つのカルチャーについての情報をインスタンス化するためパフォーマンスは向上し、
    設定クラスの中でグローバルにカルチャーについて上書きする事ができるということです。

    `sfCulterInfo::getCountries()`と`sfCulterInfo::getCurrencies()`そして`sfCultureInfo::getLanguages()`メソッドは
    戻り値を制限するためのオプションの引数をとることができるようになりました。

    PHP:
    1. ...
    2.     // will only return the FR and ES countries in english
    3.     $countries = sfCultureInfo::getInstance('en')->getCountries(array('FR', 'ES'));

    `sfCulterInfo::getCurrencies()`メソッドは通貨名の配列を返します。
    以前のバージョンのsymfonyでは、通貨記号と名前の配列を返していました。
    以前のような動作をさせるためには、次のように第2引数に`true`を渡すだけです。

    PHP:
    1. ...
    2.     $currencies = sfCultureInfo::getInstance('en')->getCurrencies(null, true);

    1つだけの国、言語、通貨の翻訳を取得するためには、`getCountry()`、`getCurrency()`、`getLanguage()`メソッドを
    `sfCultureInfo`クラスから使うことができるようになりました。

    インクルードパスの管理(Include Path Management)

    symfony 1.2はPHPの`include_path`設定を簡単に行えるようにするために
    静的メソッドである`sfToolkit::addIncludePath()`メソッドを追加します。
    このメソッドは2つのパラメーターを受け付けます。それは単一のパスかパスの配列と、
    ポジションを意味する文字列("front", "back"のどちらか)です。第2引数は標準では"front"です。

    PHP:
    1. ...
    2.     // an example from sfPropelPlugin
    3.     sfToolkit::addIncludePath(array(
    4.       sfConfig::get('sf_root_dir'),
    5.       sfConfig::get('sf_symfony_lib_dir'),
    6.       realpath(dirname(__FILE__).'/../lib/vendor'),
    7.     ));

    アドミンジェネレータ

    symfony1.2のためにアドミンジェネレータは書き直されました。そして新しいアドミンジェネレータはフォームフレームワークに基づいています。
    ジェネレータでモジュールを生成するためには、`propel:generate-admin`タスクを次のように利用します。

    C:
    1. $ php symfony propel:generate-admin backend Article

    標準では、モジュールのためにRESTルーティングを追加します。あなた自身でルーティングを作り、
    それをモデルクラス名の代わりに引数として渡すこともできます。

    [blockquote]
    $ php symfony propel:generate-admin backend article
    [/blockquote]

    設定は`generator.yml`を通して行います。全ての設定は`config`キーの下に存在します。

    CSS:
    1. ...
    2.     generator:
    3.       class: sfPropelGenerator
    4.       param:
    5.         model_class:           DemoCategory
    6.         theme:                 admin
    7.         non_verbose_templates: true
    8.         with_show:             false
    9.         singular:              ~
    10.         plural:                ~
    11.         route_prefix:          categories
    12.         with_propel_route:     1
    13.  
    14.         config:
    15.           actions: ~
    16.           fields:  ~
    17.           list:    ~
    18.           filter:  ~
    19.           form:    ~
    20.           edit:    ~
    21.           new:     ~

    `edit`と`new`の新しいフォームのための異なる設定をもつことができるようになり、それらは
    `form`設定から継承されています。

    `name`エントリーは`label`に名前が変更されました。

    古いアドミンジェネレータは後方互換性が保たれています。そのため、`propel:init-admin`タスクを実行することで利用し続けることができます。