symfony book 日本語ドキュメント

第14章 - ジェネレーター

多くのアプリケーションはデータベースに保存されたデータに基づいており、それにアクセスするためのインターフェイスを提供します。symfonyはPropelのオブジェクトに基づいたデータ操作機能を提供するモジュール作成の反復的なタスクを自動化します。オブジェクトモデルが適切に定義したのであれば、symfonyはサイト全体の管理機能(administration)も自動的に生成します。この章では、symfonyに搭載された2つのジェネレーター: scaffoldingジェネレーターとadministrationジェネレーターをお教えします。後者は完全な構文による特別な設定ファイルに依存するので、この章の多くはadministrationジェネレーターのさまざまな可能性について説明します。

モデルに基づいたコード生成

Webアプリケーションにおいて、データアクセスのオペレーションはつぎのように分類できます:

これらのオペレーションは共通なので、頭文字を取った専用の略語であるCRUD(Create、Retrieval、Update、Deletion)が存在します。多くのページはこれらの1つに還もとできます。たとえば、フォーラムのアプリケーションにおいて、最新投稿のリストは検索オペレーション(retrieve)で、投稿への返答は作成オペレーション(create)に対応します。

任意のテーブルに対するCRUDオペレーションを実装する基本的なアクションとテンプレートはWebアプリケーション内部で繰り返し作られます。symfonyにおいて、開発もしくはバックエンドインターフェイスの初期の部分を加速するために、モデルレイヤーはCRUDオペレーションを生成できるようにするための十分な情報を持ちます。

モデルに基づいたすべてのコード生成タスクはモジュール全体を作成します。これはつぎの形式のsymfonyコマンドラインへの呼び出しによって行われます:

> symfony <TASK_NAME> <APP_NAME> <MODULE_NAME> <CLASS_NAME>

コード生成タスクはpropel-init-crudpropel-generate-crudpropel-init-adminです。

scaffoldingとadministration

アプリケーションの開発期間において、2つの異なる目的のためにコード生成機能を利用できます:

symfonyのコマンドラインはscaffoldingを指定するためにcrudという単語を使い、administrationのためにadminという単語を使います。

コードを初期化するもしくは生成する

symfonyはコードを生成する方法を2つ提供します。継承(init)による方法とコード生成(generate)による方法です。

モジュールを初期化すると、symfonyから継承した空のクラスが作成され、アクションとテンプレートのPHPコードは修正されないように覆い隠されます。データ構造が完成していない場合、もしくはレコードを操作するデータベースへの素早いインターフェイスが必要な場合に便利です。実行時に実行されるコードはアプリケーションではなくキャッシュに設置されます。この種の生成用のコマンドラインタスクはpropel-init-で始まります。

初期化されたアクションのコードは空です。たとえば、初期化されたarticleモジュールはつぎのようなアクションを持ちます:

[php]
class articleActions extends autoarticleActions
{
}

一方で、修正可能なアクションとテンプレートのコードも生成できます。それゆえモジュールの結果はsymfonyのクラスから独立しているので、設定ファイルを使用して変更できません。この種類の生成用のコマンドラインタスクはpropel-generate-で始まります。

scaffoldingは開発のための土台として提供するためにビルドされるので、scaffoldingを生成することがベストであることがよくあります。一方で、administrationは設定の変更を通して更新することが容易なので、データモデルを変更した場合でも有効です。administrationが初期化のみ行われる理由はそういうわけです。

データモデルの例

この章全体を通して、リストはシンプルな例に基づいたsymfonyのジェネレーターの機能を示します。これによって8章を思い出すでしょう。これは2つのArticleクラスとCommentクラスを含む、Webログのアプリケーションのよく知られた例です。図14-1で描かれているように、リスト14-1はスキーマを示します。

リスト14-1 - blogアプリケーションのschema.ymlの例

propel:
  blog_article:
    _attributes: { phpName: Article }
    id:
    title:       varchar(255)
    content:     longvarchar
    created_at:
  blog_comment:
    _attributes: { phpName: Comment }
    id:
    article_id:
    author:      varchar(255)
    content:     longvarchar
    created_at:

図14-1 データモデルの例

データモデルの例

コード生成を可能にするためにスキーマ作成の期間で従わなければならない特別なルールは存在しません。symfonyはスキーマをそのまま使用し、scaffoldingもしくはadministrationを生成するためにスキーマの属性を解釈します。

TIP この章を最大限に活用するには、例の内容を実際に行う必要があります。リストで説明されたすべてのステップを眺めるのであれば、symfonyが何を生成し、生成されたコードで何が行われるのかということをより理解できるようになります。以まえに説明されたようなデータ構造を作成したり、blog_articleblog_commentテーブルをともなうデータベースを作成したり、サンプルデータをこのデータベースに投入したくなるでしょう。

scaffolding

scaffoldingは初期のアプリケーション開発において大いに役立ちます。1つのコマンドで、symfonyは任意のテーブルの記述に基づいてモジュール全体を作成します。

scaffoldingを生成する

Articleモデルクラスに基づいたarticleモジュール用のscaffoldingを生成するには、つぎのコマンドを入力します:

> symfony propel-generate-crud myapp article Article

symfonyはschema.ymlのなかのArticleクラスの定義を読み込み、myapp/modules/article/ディレクトリのなかでそれをもとにテンプレートとアクションのセットを作ります。

生成モジュールはビューを3つ含みます。listビューはデフォルトのビューで、図14-2で再現されたhttp://localhost/myapp_dev.php/articleをブラウジングするとき、blog_articleテーブルの列を表示します。

図14-2 - articleモジュールのlistビュー

articleモジュールのlistビュー

showビューを表示する記事の識別子をクリックしてください。図14-3のように1つのページで一列の詳細な情報が表示されます。

図14-3 - articleモジュールのshowビュー

articleモジュールのshowビュー

edit linkをクリックすると記述内容を編集する、もしくはlistビュー内部のcreate linkをクリックして新しい記事を作ると、図14-4で再現されたeditビューが表示されます。

このモジュールを利用することで、新しい記事を作成する、既存の記事を修正するもしくは削除することができます。生成されたコードはさらなる開発のためのよい土台です。リスト14-2は新しいモジュールの生成されたアクションとテンプレートの一覧を示しています。

図14-4 - articleモジュールのeditビュー

articleモジュールのeditビュー

リスト14-2 - 生成されたCRUD要素(myapp/modules/article/)

// actions/actions.class.phpにおいて
index           // つぎのlistアクションにフォワードする
list            // テーブルのすべてのレコードのリストを表示する
show            // 1つのレコードのすべてのカラムのリストを表示する
edit            // 1つのレコードのカラムを修正するために一つのフォームを表示する
update           // editアクションフォームによって呼び出されたアクション
delete           // レコードを削除する
create           // 新しいレコードを作成する

// テンプレートにおいて/
editSuccess.php  // レコード編集フォーム(editビュー)
listSuccess.php  // すべてのレコードのリスト(listビュー)
showSuccess.php  // 1つのレコードの詳細 (showビュー)

これらのアクションとテンプレートのロジックのすべてを再生成して説明するよりも、はるかにシンプルで明快です。リスト14-3は生成されたアクションのクラス内部で生成されたものの一部を示しています。

リスト14-3 - 生成されたActionクラス(myapp/modules/article/actions/actions.class.php)

[php]
class articleActions extends sfActions
{
  public function executeIndex()
  {
    return $this->forward('article', 'list');
  }

  public function executeList()
  {
    $this->articles = ArticlePeer::doSelect(new Criteria());
  }

  public function executeShow()
  {
    $this->article = ArticlePeer::retrieveByPk($this->getRequestParameter('id'));
    $this->forward404Unless($this->article);
  }
  ...

あなたの要件に満たすように生成されたコードを修正し、情報のやりとりをしたいすべてのテーブルに対して、CRUDの生成を繰り返せば、基本的な作業用アプリケーションが手に入ります。scaffoldingを生成することで開発が大いにはかどります; 汚い仕事はsymfonyに任せ、インターフェイスと仕様に集中しましょう。

scaffoldingを初期化する

データベースのデータにアクセスできることを確認する必要があるとき、scafoldingの初期化がもっとも役立ちます。すべてが立派に動作していることを確認したらビルドして削除することを素早く行うことができます。

Articleクラス名のレコードを処理するarticleモジュールを作成するPropel のscaffoldingを初期化するには、つぎのコマンドを入力します:

> symfony propel-init-crud myapp article Article

デフォルトのアクションを使用してlistビューにアクセスできます:

http://localhost/myapp_dev.php/article

ページの結果は生成されたscaffoldingのためのものとまったく同じです。これらをデータベースへのシンプルなWebインターフェイスとして使うことができます。

articleモジュール内部で新しく作成されたactions.class.phpを確認すると、これが空であることがわかります: すべての内容は自動生成されたクラスから継承されたものです。テンプレートにも同じことがあてはまります: templates/ディレクトリ内部にはテンプレートファイルは存在しません。初期化されたアクションとテンプレートの背後にあるコードは生成されたscaffooldingに関して同じですが、これらはアプリケーションのキャッシュ(myproject/cache/myapp/pord/module/autoArticle/)内部だけに存在します。

アプリケーションの開発期間において、インターフェイスにかかわらず、開発者はデータと情報のやりとりを行うscaffoldingを初期化します。コードをカスタマイズすることは想定されていません; 初期化されたscaffoldingはデータを管理するphpMyadminのシンプルな代替物とみなすことができます。

administration

symfonyは、アプリケーションのバックエンドのために、schema.ymlファイルからのモデルクラス定義に基づいて、高度なモジュールを作成できます。生成されたadministrationモジュールだけを用いてサイト全体のadministrationを作成できます。このセクションの例においてbackendアプリケーションに追加されたadministraionモジュールを説明します。プロジェクトがbackendアプリケーションを持たない場合、init-appタスクを呼び出してスケルトンを作成してください:

> symfony init-app backend

administrationモジュールはgenerator.ymlという名前の特別な設定ファイルを通してモデルを解釈します。すべての生成されたコンポーネントとモジュールの外見を拡張するためにgenerator.ymlファイルを変更できます。このようなモジュールは通常のモジュールメカニズムからの恩恵を受けます(レイアウト、バリデーション、ルーティング、カスタム設定、オートロードなど)。独自機能を生成されたadministrationに統合するために、生成されたアクションもしくはテンプレートをオーバーライドすることもできますが、generator.ymlはもっとも共通の要件を考慮して、PHPのコードの使いかたを限定的なものにします。

administrationモジュールを初期化する

symfonyによって、モジュールを基本単位としてadministrationをビルドします。propel-init-adminタスクを用いることで、モジュールはPropelのオブジェクトに基づいて生成されます。propel-init-adminタスクはscaffoldingを初期化するために使われる構文と似た構文を使います:

> symfony propel-init-admin backend article Article

この呼び出しだけでbackendアプリケーションのなかにArticleクラスの定義に基づいたarticleモジュールが作成されるので、つぎのURLからアクセスできます:

http://localhost/backend.php/article

図14-5、図14-6で描かれている生成されたモジュールの外見は商業用のアプリケーションとしてそのまま利用できるほど十分に洗練されています。

図14-5 - backendアプリケーション内部のarticleモジュールのlistビュー

backendアプリケーション内部のarticleモジュールのlistビュー

図14-6 - バックエンドでarticleモジュールのeditビュー

バックエンドでarticleモジュールのeditビュー

administrationのインターフェイスとscaffoldingのインターフェイスの違いは現時点は顕著には見えないかもしれませんが、administrationの柔軟な設定によってPHPのコードを使わずに多くの追加機能を持つ基本的なレイアウトを強化できます。

NOTE administrationモジュールだけを初期化することができます(生成はできません)。

生成されたコードを見る

apps/backend/modules/article/ディレクトリ内のadministrationのArticleモジュールのコードは初期化だけ行われたので空です。このモジュールの生成されたコードを吟味するための最良の方法はブラウザーを利用してこれと情報のやりとりをすることとcache/フォルダーの内容を確認することです。リスト14-4はキャッシュで見つかる生成されたアクションとテンプレートのリストを表示します。

リスト14-4 - 生成されたadministrationの要素(cache/backend/ENV/modules/article/)

// actions/actions.class.phpにおいて
create           // editにフォワードする
delete            //レコードを削除する
edit             // レコードのフィールドを修正するためにフォームを表示する
                 // フォーム投稿を扱う
index            // listに進む
list             // テーブルのすべてのレコードのリストを表示する
save             // editに進む

// templates/において
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
editSuccess.php
listSuccess.php

これは生成されたadministrationモジュールがおもに2つのビュー、editビューとlistビューで構成されることを示します。コードを見てみると、モジュール性が非常に高く、読みやすく拡張性のあるものであることがわかります。

generator.yml設定ファイルを導入する

(administrationで生成されたモジュールはshowアクションを持たないこということは別にして)scaffoldingとadministrationの主要な違いはadministrationがYAML形式のgenerator.yml設定ファイルで見つかるパラメーターに依存することです。新しく生成されたadministrationモジュールのデフォルト設定を見るには、リスト14-5で再現されている、backend/modules/article/config/generator.ymlディレクトリに設置されたgenerator.ymlファイルを開いてください。

リスト14-5 - ジェネレーターのデフォルト設定(backend/modules/article/config/generator.yml)

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

この設定は基本的なadministrationを生成するために十分なものです。カスタマイズされた内容はthemeの行の後のparamキーの下に追加されます(generator.ymlファイルの底に追加されたすべての行は少なくとも適切にインデントされた4つの空白スペースで始めなければならないことを意味します)。リスト14-6はカスタマイズされた典型的なgenerator.ymlを示しています。

リスト14-6 - 典型的なジェネレーターの完全な設定

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      author_id:      { name: Article author }

    list:
      title:          List of all articles
      display:        [title, author_id, category_id]
      fields:
        published_on: { params: date_format='dd/MM/yy' }
      layout:         stacked
      params:         |
        %%is_published%%<strong>%%=title%%</strong><br /><em>by %%author%%
        in %%category%% (%%published_on%%)</em><p>%%content_summary%%</p>
      filters:        [title, category_id, author_id, is_published]
      max_per_page:   2

    edit:
      title:          Editing article "%%title%%"
      display:
        "Post":       [title, category_id, content]
        "Workflow":   [author_id, is_published, created_on]
      fields:
        category_id:  { params: disabled=true }
        is_published: { type: plain}
        created_on:   { type: plain, params: date_format='dd/MM/yy' }
        author_id:    { params: size=5 include_custom=>> Choose an author << }
        published_on: { credentials:  }
        content:      { params: rich=true tinymce_options=height:150 }

つぎのセクションでこの設定ファイルで利用可能なすべてのパラメーターの詳細内容を説明します。

ジェネレーターの設定

ジェネレーターの設定ファイルはとても強力で、生成されたadministrationを多くの方法で変更できます。しかしこの機能には代償があります: 全体の構文の記述が読んで学ぶには長いので、文章で説明したら、この章がこの本でもっとも長くなってしまいます。ですのでsymfonyのWebサイトはこの構文を学ぶための助けになる追加リソースを提示します: administrationジェネレーターのチートシートが図14-7で再現されてます。http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdfから保存し、この章のつぎの例を読むときにこれを頭のなかにとどめておいてください。

このセクションの例は、Commentクラス定義に基づいて、administrationのcommentモジュールと同様に、administrationのarticleモジュールを調整します。propel-init-adminタスクで後者を作成してください:

> symfony propel-init-admin backend comment Comment

図14-7 - administrationジェネレーターのチートシート

administrationジェネレーターチートのシート

フィールド

デフォルトでは、listビューのカラムとeditビューのフィールドはschema.ymlで定義されたカラムです。generator.ymlによって、どのフィールドが表示され、隠されるかを選ぶことが可能で、これらがオブジェクトモデルに直接対応していなくても、独自のフィールドを追加できます。

フィールドの設定

administrationジェネレーターはschema.ymlファイルのカラムごとにfieldを作ります。fieldsキーの下で、それぞれのフィールドの表示方法やフォーマット方法などを修正できます。たとえば、リスト14-7で示されるフィールドの設定はtitleフィールド用のカスタムラベルクラスと入力タイプ、そしてcontentフィールド用のラベルとツールチップを定義します。つぎのセクションでそれぞれのパラメーターが動作する方法を詳細に説明します。

リスト14-7 - カラムに対してカスタムラベルを設定する

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title, type: textarea_tag, params: class=foo }
      content:        { name: Body, help: Fill in the article body }

リスト14-8でお手本が示されているように、すべてのビューに対するこのデフォルトの定義に加えて、任意のビュー(listedit)のためのフィールドの設定をオーバーライドできます。

リスト14-8 - ビュー単位でグローバルな設定ビューをオーバーライドする

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title }
      content:        { name: Body }

    list:
      fields:
        title:        { name: Title }

    edit:
      fields:
        content:      { name: Body of the article }

一般的な原則があります: fieldsキーの下のモジュール全体に対して設定される任意の設定も後に続くビュー固有の領域(listedit)でオーバーライドできます。

フィールドをdisplay設定に追加する

fieldsセクションで定義したフィールドはそれぞれのビューに対して表示、隠す、順番に並べるなど、さまざまな方法で分類できます。displayキーはこの目的のために使われます。たとえば、commentモジュールのフィールドを順番に並べるには、リスト14-9のコードを使います。

リスト14-9 - 表示フィールドを選択する(modules/comment/config/generator.yml)

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    fields:
      article_id:     { name: Article }
      created_at:     { name: Published on }
      content:        { name: Body }

    list:
      display:        [id, article_id, content]

    edit:
      display:
        NONE:         [article_id]
        Editable:     [author, content, created_at]

図14-8のようにlistは3つのカラムを表示し、図14-9のように、editフォームは2つのグループに集められた4つのフィールドを表示します。

図14-8 - commentモジュールのlistビュー内部のカスタムカラム設定

commentモジュールのlistビュー内部のカスタムカラム設定

図14-9 - commentモジュールのeditビュー内部でフィールドを分類する

commentモジュールのeditビュー内でフィールドを分類する

display設定を2つの方法で利用できます:

TIP デフォルトでは、主キーのカラムはどちらのビューでも決して現れません。

カスタムフィールド

当然のことながら、generator.ymlで設定されたフィールドはスキーマで定義された実際のカラムに対応している必要はありません。関連するクラスがカスタムゲッターを提供する場合、これをlistビューのためのフィールドとして使うことができます; ゲッターかつ/もしくはセッターが存在する場合、このクラスはeditビューでも利用できます。たとえば、リスト14-10のように、ArticleモデルをgetNbComments()メソッドで拡張できます。

リスト14-10 - カスタムゲッターをモデルに追加する(lib/model/Article.class.php)

[php]
public function getNbComments()
{
  return $this->countComments();
}

リスト14-11のように、nb_commentsは生成されたモジュールのなかのフィールドとして利用できます(ゲッターはcamelCaseバージョンのフィールド名を使います)。

リスト14-11 - カスタムゲッターはadministrationモジュールに対して追加カラムを提供する(backend/modules/article/config/generator.yml)

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    list:
      display:        [id, title, nb_comments, created_at]

articleモジュールのlistビューの結果は図14-10で示されます。

図14-10 - articleモジュールのlistビュー内のカスタムフィールド

articleモジュールのlistビュー内部のカスタムフィールド

カスタムフィールドは生のデータ以上のものを表示するためにHTMLのコードも返します。たとえば、リスト14-12で示されるようなCommentクラスをgetArticleLink()メソッドで拡張できます。

リスト14-12 - HTMLを返すカスタムゲッターを追加する(lib/model/Comment.class.php)

[php]
public function getArticleLink()
{
  return link_to($this->getArticle()->getTitle(), 'article/edit?id='.$this->getArticleId());
}

リスト14-11と同じ構文でこの新しいゲッターをcomment/listビュー内のカスタムフィールドとして使うことができます。リスト14-13の例と、図14-11の結果をご覧ください。ゲッター(記事のハイパーリンク)によって出力されるHTMLのコードは記事の主キーの代わりに2番目のカラムに現れます。

リスト14-13 - HTML結果を返すカスタムゲッターは追加カラムとして使うこともできる(modules/comment/config/generator.yml)

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    list:
      display:        [id, article_link, content]

図14-11 - commentモジュールのlistビュー内部のカスタムフィールド

commentモジュールのlistビュー内部のカスタムフィールド

部分テンプレートフィールド

モデルに設置されたコードはプレゼンテーションから独立していなければなりません。初期のgetArticleLink()メソッドの例はレイヤー分離の原則を順守していません。ビューのコードのなかにはモデルレイヤーに現れるものがあるからです。正しい方法で、同じ結果を実現するには、部分テンプレート内部のカスタムフィールドに対してHTMLを出力するコードを設置したほうがベターです。幸いして、administrationジェネレーターによってアンダースコアのプレフィックスを持つフィールド名を宣言できます。この場合、リスト14-13のgenerator.ymlファイルはリスト14-14のように修正されます。

リスト14-14 - _のプレフィックスをつけることで部分テンプレートを追加カラムとして使うことができる、

    list:
      display:        [id, _article_link, created_at]

これを機能させるには、リスト14-15で示されるように、_article_link.php部分テンプレートをmodules/comment/tempaltes/ディレクトリのなかに作らなければなりません。

リスト14-15 - listビューのための部分テンプレートの例(modules/comment/templates/_article_link.php)

<?php echo link_to($comment->getArticle()->getTitle(), 'article/edit?id='.$comment->getArticleId()) ?>

部分テンプレートフィールドの部分テンプレートテンプレートはクラスによって命名された変数(この例では$comment)を通して現在のオブジェクトにアクセスできることに注意してください。たとえば、UserGroupという名前のクラスのためにビルドされたモジュールに対して、部分テンプレートは$user_group変数を通して現在のオブジェクトにアクセスできるようになります。

レイヤー分離の原則が順守されることを除いて、結果は図14-11と同じです。レイヤー分離の原則を順守することに慣れたら、アプリケーションはより維持しやすくなります。

部分テンプレートフィールドのパラメーターをカスタマイズする必要がある場合、fieldキーの下で通常のフィールドと同じ事を行います。アンダースコア(_)をキーの先頭に含めないでください。リスト14-16をご覧ください。

リスト14-16 - 部分テンプレートフィールドのプロパティはfieldsキーの下でカスタマイズできる

      fields:
        article_link:   { name: Article }

部分テンプレートがロジックで混雑するようになったら、おそらくはこれをコンポーネントで置き換えたいと思うでしょう。リスト14-17のように、_のプレフィックスを~に変更することで、コンポーネントフィールドを定義できます。

リスト14-17 - コンポーネントは追加のカラムとして利用できる。~のプレフィックスを使う

    ...
    list:
      display:        [id, ~article_link, created_at]

生成されたテンプレートにおいて、現在のモジュールのarticleLinkコンポーネントを呼び出すことでこれは生成されます。

NOTE カスタムと部分テンプレートのフィールドはlistビュー、editビューのなかと、フィルターに対して利用できます。いくつかのビューに対して同じ部分テンプレートを使う場合、コンテキスト('list''edit'、もしくは 'filter')が$type変数に保存されます。

ビューのカスタマイゼーション

editビューとlistビューの外見を変更するために、テンプレートを変えたいと思うことがあります。しかしこれらは自動的に生成されるので、これを行うのはあまりよいアイディアではありません。代わりにgenerator.yml設定ファイルを使用すべきです。モジュール性を損なうことなくほとんどすべての必要なことを行うことができるからです。

ビューのタイトルを変更する

フィールドのカスタムセットに加えて、listページとeditページはカスタムページタイトルを持ちます。たとえば、articleビューのタイトルをカスタマイズしたい場合、リスト14-18のように行います。editビューの結果は図14-12に描かれています

リスト14-18 - それぞれのビューに対してカスタムタイトルを設定する(backend/modules/article/config/generator.yml)

    list:
      title:          List of Articles
      ...

    edit:
      title:          Body of article %%title%%
      display:        [content]

図14-12 - articleモジュールのeditビュー内部のカスタムタイトル

articleモジュールのeditビュー内部のカスタムタイトル

デフォルトのタイトルはクラスの名前を使うので、モデルが明白なクラスの名前を使うのであれば、これらのクラス名で十分であることはよくあります。

TIP generator.ymlの文字列の値において、フィールドの値は%%で囲まれたフィールドの名前を通してアクセスできます。

ツールチップを追加する

listeditビュー内部において、表示されるフィールドを記述するための助けになるツールチップを追加できます。たとえば、ツールチップをcommentモジュールのeditビューのarticle_idフィールドに追加するには、リスト14-19のように、helpプロパティをfields定義のなかに追加します。結果は図14-13に示されています。

リスト14-19 - editビューでツールチップを設定する(modules/comment/config/generator.yml)

    edit:
      fields:
        ...
        article_id:   { help: The current comment relates to this article }

図14-13 - commentモジュールのeditビュー内部のツールチップ

commentモジュールのeditビュー内部のツールチップ

listビューにおいて、ツールチップはカラムヘッダーに表示されます; editビュー内において、これらはinputの下に現れます。

日付の書式を修正する

リスト14-20でお手本が示されているように、date_formatパラメーターを使い始めると同時にカスタム書式で日付を表示できます。

リスト14-20 listビューのなかで日付の書式設定をする

    list:
      fields:
        created_at:         { name: Published, params: date_format='dd/MM' }

このパラメーターは以前の章で説明されたformat_date()ヘルパーと同じフォーマットパラメーターをとります。

SIDEBAR administrationテンプレートは国際化の準備ができています

生成されたテンプレートのなかで見つかるすべてのテキストは自動的に国際化されます(たとえば、__()ヘルパーへの呼び出しに囲まれます)。このことは、以前の章で説明されたように、apps/myapp/i18n/ディレクトリ内において、語句の翻訳をXLIFFファイルに追加することで、生成されたadministrationを簡単に翻訳できることを意味します。

listビュー固有のカスタマイズ

listビューは、表形式、もしくはすべての詳細な内容が一行に積み重ねられた状態で、レコードの詳細を表示できます。これはフィルター、パジネーションとソート機能も含みます。これらの機能は設定によって変更できます。詳細はつぎのセクションで説明します。

レイアウトを変更する

デフォルトでは、listビューとeditビュー間のハイパーリンクは主キーのカラムによって生じています。図14-11に戻ると、コメントリストのidカラムがそれぞれのコメントの主キーを表示するだけでなく、ユーザーがeditビューにアクセスすることを許可するハイパーリンクを提供します。

別のカラム上に現れるレコードの詳細へのハイパーリンクが望ましいのであれば、displayキーの等号(=)をプレフィックスとしてカラムの名前に追加します。リスト14-21はlistコメントの表示されるフィールドからidを除去して代わりにcontentフィールド上にハイパーリンクを置く方法を示しています。図14-14のスクリーンショットを確認してください。

リスト14-21 - editビューのためのハイパーリンクをlistビューに移動させる(modules/comment/config/gererator.yml)

    list:
      display:    [article_link, =content]

図14-14 - commentモジュールのlistビューのなかで、リンクを別のカラム上のeditビューに移動させる

commentモジュールのlistビュー内で、リンクを別のカラム上のeditビューに移動させる

デフォルトでは、以前示されたように、listビューは、フィールドがカラムとして表示されるtabularレイアウトを使います。しかしstackedレイアウトを使用してフィールドをテーブルの全長を詳しく記述する単独の文字列に連結することもできます。stackedレイアウトを選ぶ場合、paramsキーにリストのそれぞれの行の値を定義するパターンを組み込まなければなりません。たとえば、リスト14-22はcommentモジュールのlistビューに対してstackedレイヤーを定義します。結果は図14-15です。

リスト14-22 - listビューでstackedレイアウトを使う(modules/comment/cofig/generator.yml)

    list:
      layout:  stacked
      params:  |
        %%=content%% <br />
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]

図14-15 - commentモジュールのlistビュー内部のstackedレイアウト

commentモジュールのlistビュー内のstackedレイアウト

tabularレイアウトはdisplayキーの下でフィールドの配列を必要としますが、stackedレイアウトはそれぞれのレコードに対して生成されたHTMLコードのためにparamsキーを使うことに留意してください。しかしながら、インタラクティブなソートに対して利用できるカラムヘッダーを決定するためにdisplay配列がstackedレイアウトでも使われます

結果をフィルタリングする

listビューにおいて、フィルターのインタラクションのセットを追加できます。これらのフィルターによって、ユーザーは結果をより少なく表示して速く望むものに到達できます。フィールド名の配列で、filtersキーの下にフィルターを設定します。たとえば、図14-16のフィルターボックスと同じようなものを表示するには、リスト14-23のように、article_idauthorフィールド、とcreated_atフィールド上のフィルターをコメントのlistビューに追加します。これを機能させるには、__toString()メソッドをArticleクラス(たとえば、記事のtitleを返す)に追加する必要があります。

リスト14-23 - listビューでフィルターを設定する(modules/comment/config/generator.yml)

    list:
      filters: [article_id, author, created_at]
      layout:  stacked
      params:  |
        %%=content%% <br />
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]

図14-16 - commentモジュールのlistビュー内部のフィルター

commentモジュールのlistビュー内部のフィルター

symfonyによって表示されるフィルターはカラム型に依存します:

リストのなかで部分テンプレートフィールドを使うことと同じように、symfonyが独自に処理できないフィルターを作成するために部分テンプレートフィルターも利用できます。たとえば、2つの値(openclosed)だけ含むstateフィールドが存在するが、何らかの理由で、テーブルのリレーションを使う代わりにこれらの値をフィールドに直接保存する理由がある場合を想像してください。このフィールドのシンプルなフィールド(のstring型)はテキストベースの検索ですが、あなたが欲しいと思うものはおそらく値のドロップダウンリストです。これを部分テンプレートフィルターで実現することは簡単です。リスト14-24の実装例をご覧ください。

リスト14-24 - 部分テンプレートフィルターを使う

[php]
// templates/_state.phpにおいて、部分テンプレートを定義する
<?php echo select_tag('filters[state]', options_for_select(array(
  '' => '',
  'open' => 'open',
  'closed' => 'closed',
), isset($filters['state']) ? $filters['state'] : '')) ?>

// config/generator.ymlにおいて部分テンプレートフィルターをフィルターリストに追加する
    list:
      filters:        [date, _state]

部分テンプレートが$filtersの値にアクセス可能で、フィルターの現在の値を取得するために便利であることを覚えておいてください。

空の値を探すためにとても便利な最後の1つのオプションがあります。著者を持たないコメントを表示するコメントのリストをフィルタリングしたい場合を想像してください。問題はauthorのフィルターを空のままにしておく場合、これが無視されることです。解決方法はリスト14-25のように、filter_is_emptyフィールド設定をtrueに設定と、フィルターは追加のチェックボックスを表示するので、図14-17で図示されているように、空の値を探すことができます。

リスト14-25 - listビュー内部のauthorフィールドの上に空の値のフィルタリングを追加する

    list:
      fields:
        author:   { filter_is_empty: true }
      filters:    [article_id, author, created_at]

図14-17 - 空のauthorの値をフィルタリングすることを可能にする

空のauthorの値をフィルタリングすることを可能にする

リストをソートする

listビューにおいて、図14-18で示されるように、テーブルのヘッダーはリストを再び順番に並べるために使われるハイパーリンクです。これらのヘッダーはtabularレイアウトとstackedレイアウトの両方で表示されます。これらのリンクをクリックするとリストの順番を再配置するsortパラメーターでページがリロードされます。

図14-18 - listビューのテーブルヘッダーはソートをコントロールする

listビューのテーブルヘッダーはソートはコントロールする

カラムにしたがって直接ソートされるリストを指し示す構文を再利用できます:

[php]
<?php echo link_to('日付順のコメントリスト', 'comment/list?sort=created_at&type=desc' ) ?>

generator.ymlファイル内部のlistビューに対して直接デフォルトのsortの順番を定義することも可能です。構文はリスト14-26で示された例に従います。

リスト14-26 - listビューのなかでデフォルトのsortフィールドを設定する

    list:
      sort:   created_at
      # ソートの順番を指定するための、代替構文
      sort:   [created_at, desc]

NOTE 実際のカラムに対応するフィールドだけが、カスタムもしくは部分テンプレートフィールドではなく、ソートのコントロール機能に変換されます。

パジネーションをカスタマイズする

生成されたadministrationは大きなテーブルさえも効率的に処理します。listビューがデフォルトでパジネーションを使うからです。テーブル内の実際の列の数がページごとの列の最大数を越えるとき、パジネーションのコントロール機能がリストの底に現れます。たとえば、図14-19はページごとに表示される5つのコメントの制限ではなく、テーブル内で6つのテストのコメントを持つコメントのリストを表示しますが、ページごとに表示されるコメント数は最大5まで制限されています。パジネーションはよいパフォーマンスとユーザービリティを保証します。データベースから表示される列のみを効率的に検索し、administrationモジュールによって何百万の列をかかえるテーブルでさえも管理できるからです。

図14-19 - パジネーションのコントロール機能は長いリスト上に現れる

パジネーションのコントロール機能は長いリスト上に現れる

max_per_pageパラメーターによってそれぞれのページが表示されるレコードの数をカスタマイズできます:

    list:
      max_per_page:   5

ページ配信を加速するためにJoinを使う

デフォルトでは、administrationジェネレーターはレコードのリストを検索するdoSelect()を使います。しかし、リストで関連するオブジェクトを使う場合、リストを表示するために必要なデータベースのクエリの回数が急に増えることがあります。たとえば、コメントのリストで記事の名前を表示したい場合、関連するArticleオブジェクトを検索するために追加クエリがリスト内のそれぞれの投稿に対して必要です。ですので、クエリの数を最適化するために、doSelectJoinXXX()メソッドを使うページャーを強制したい場合があるかもしれません。これはpeer_methodパラメーターで指定できます。

    list:
      peer_method:   doSelectJoinArticle

18章ではJoinの概念についてより広範囲に説明します。

editビュー固有のカスタマイズ

editビューにおいて、ユーザーは任意のレコードのためにそれぞれのカラムの値を修正できます。symfonyはカラムのデータ型にしたがって表示する入力のタイプを決定します。symfonyはobject_*_tag()ヘルパーを生成し、そのヘルパーに編集するオブジェクトとプロパティを渡します。たとえば、ユーザーがtitleフィールドを編集できることを記事のeditビューの設定が保証する場合です:

    edit:
      display: [title, ...]

このカラムはスキーマでvarchar型として定義されているので、editページはタイトルを編集するための通常のテキスト入力タグを表示します。

[php]
<?php echo object_input_tag($article, 'getTitle') ?>

入力タイプを変更する

デフォルトの型からフィールドへの変換ルールはつぎのとおりです:

任意のフィールドに対してカスタム入力タイプを指定するためにこれらのルールをオーバーライドしたい場合を考えます。その範囲で、fields定義内のtypeパラメーターを特定のフォームヘルパー名に設定します。生成されたobject_*_tag()オプションに関しては、paramsパラメーターで変更可能です。リスト14-27の例をご覧ください。

リスト14-27 - editビューに対してカスタム入力タイプとパラメーターを設定する

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    edit:
      fields:
                      ## 入力をドロップし、プレーンテキストを表示するだけ
        id:           { type: plain }
                      ## 入力は編集可能ではない
        author:       { params: disabled=true }
                      ## 入力はテキストエリアT(object_textarea_tag)
        content:      { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 }
                      ## 入力は選択 (object_select_tag)
        article_id:   { params: include_custom=Choose an article }
         ...

paramパラメーターは生成されたobject_*_tag()にオプションとして渡されます。たとえば、先のarticle_idに対するparamsの定義はつぎのようなテンプレートのなかで生成されます:

[php]
<?php echo object_select_tag($comment, 'getArticleId', 'related_class=Article', 'include_custom=Choose an article') ?>

このことは通常フォームヘルパーで利用できるすべてのオプションはeditビューでカスタマイズできることを意味します。

部分テンプレートフィールドを扱う

部分テンプレートフィールドはlistビューのようにeditビューで使用できます。違いは、アクションにおいて、部分テンプレートフィールドによって送られたリクエストパラメーターの値にしたがって、カラムの更新を手動で扱わなければならないことです。symfonyは(実際のカラムに対応する)通常のフィールドを処理する方法を理解していますが、部分テンプレートフィールド内にインクルードした入力を処理する方法を推測することはできません。

たとえば、利用可能なフィールドがidnickname、とpasswordであるUserクラスのためのadministrationモジュールを想像してください。サイトの管理者はリクエスト上のユーザーのパスワードを変更できなければなりませんが、editビューはセキュリティの理由からパスワードフィールドの値を表示してはなりません。代わりに、値を変更するためにサイトの管理者が入力できる空のパスワード入力を表示します。このようなeditビューのためのジェネレーターの設定はリスト14-28と同じです。

リスト14-28 - 部分テンプレートフィールドをeditビューに含める

    edit:
      display:        [id, nickname, _newpassword]
      fields:
        newpassword:  { name: Password, help: Enter a password to change it, leave the field blank to keep the current one }

templates/_newpassword.php部分テンプレートはつぎのようなコードを含みます:

[php]
<?php echo input_password_tag('newpassword', '') ?>

この部分テンプレートは、オブジェクトフォームヘルパーではなく、シンプルなフォームヘルパーを使うことに注意してください。フォームの入力を投入するために現在のUserオブジェクトからパスワードの値を検索するとユーザーのパスワードが公開されてしまう可能性があるので、望ましくないからです。

では、アクションのなかでオブジェクトを更新するためにこのコントロール機能から値を使うには、アクションのなかでupdateUserFromrequest()メソッドを拡張する必要があります。これを行うには、リスト14-29のように、部分テンプレートフィールドの入力に対してカスタムふるまいを持つアクションのファイルのなかに同じ名前を持つメソッドを作ります。

リスト14-29 - アクションの部分テンプレートフィールドを処理する(modules/user/actions/actions.class.php)

[php]
class userActions extends autouserActions
{
  protected function updateUserFromRequest()
  {
    // 部分テンプレートフィールドの入力を扱う
    $password = $this->getRequestParameter('newpassword');

    if ($password)
    {
      $this->user->setPassword($password);
    }

    // symfonyに別のフィールドを扱う
    parent::updateUserFromRequest();
  }
}

NOTE 実際の世界において、通常の場合、間違った入力を避けるためにuser/editビューは2つのpasswordフィールドを含みます。実際には、10章で見た通り、これはバリデーターを通して行われます。administraionによって生成されたモジュールは通常のモジュールと同じようにこのメカニズムから恩恵を受けます。

外部キーを扱う

スキーマがテーブルのリレーションを定義する場合、生成されたadministrationモジュールはこれを活用して、より自動化されたコントロール方法を提供するので、リレーションの管理作業が大いに簡素化されます。

一対多のリレーション

1対多のテーブルのリレーションはadministrationジェネレーターによって考慮されます。以前の図14-1で記述されたように、blog_commentテーブルはarticle_idフィールドを通してblog_articleテーブルとの関係を持ちます。Commentクラスのモジュールをadministrationジェネレーターによって初期化する場合、comment/editアクションはblog_articleテーブル内の利用可能なレコードのIDを示すarticle_idをドロップダウンリストとして表示します(説明図は図14-9を再度確認)。

加えて、Articleオブジェクトで__toString()メソッドを定義する場合、ドロップダウンのオプションのテキストは主キーの代わりにこのメソッドを使います。

articleモジュール(多対一のリレーション)内部の記事に関連するコメントのリストを表示する場合、部分テンプレートフィールドの方法によってモジュールを少しカスタマイズする必要があります。

多対多のリレーション

symfonyはテーブルの多対多のリレーションも考慮しますが、これらをスキーマで定義できないので、いくつかのパラメーターをgenerator.ymlファイルに追加する必要があります。

多対多のリレーションの実装は中間のテーブルを必要とします。たとえば、blog_articleblog_authorの間に多対多のリレーションがある場合(記事は複数の著者によって書かれ、あきらかに、著者は複数の記事を書くことができる)、もしくは図14-20と同じように、データベースはつねにblog_article_authorと呼ばれるテーブルで終わります。

図14-20 - 多対多のリレーションを実装するためにthrough_classを利用する

多対多のリレーションを実装するためにthrough_classを利用する

モデルはArticleAuthorと呼ばれるクラスを持ち、administrationジェネレーターが必要とする唯一のものです。しかし、これをフィールドのthrough_classパラメーターとして渡さなければなりません。

たとえば、Articleクラスに基づいて生成されたモジュールにおいて、リスト14-30のようにgenerator.ymlを書く場合、Authorクラスによる新しい多対多のリレーションを作るためにフィールドを追加できます。

リスト14-30 - through_classパラメーターで多対多のリレーションを扱う

    edit:
      fields:
        article_author: { type: admin_double_list, params: through_class=ArticleAuthor }

フィールドは既存のオブジェクト間のリンクを処理するので、通常のドロップダウンリストは十分ではありません。そのためには特別な入力タイプを使わなければなりません。symfonyは2つのリストの関連するメンバーを助けする3つのウィジェットを提供します(図14-21に描かれています):

図14-21 - 多対多のリレーションに対して利用できるコントロール機能

多対多のリレーションに対して利用できるコントロール機能

インタラクションを追加する

administrationモジュールはユーザーが通常のCRUDオペレーションを実行できるようにしますが、ビューに対して独自のインタラクションを追加するもしくは可能なインタラクションを制限することもできます。たとえば、リスト14-31で示されているインタラクションを定義することでarticleモジュール上のデフォルトのすべてのCRUDアクションにアクセスできるようになります。

リスト14-31 - それぞれのビューに対してインタラクションを定義する(backend/modules/article/config/generator.yml)

    list:
      title:          List of Articles
      object_actions:
        _edit:         ~
        _delete:       ~
      actions:
        _create:       ~

    edit:
      title:          Body of article %%title%%
      actions:
        _list:         ~
        _save:         ~
        _save_and_add: ~
        _delete:       ~

listビューにおいて、2つのアクションの設定が存在します: すべてのオブジェクトに対して利用可能なアクションのリストと全体のページに対して利用可能なアクションのリストです。リスト14-31で定義されたlistインタラクションは図14-22のようにレンダリングされます。それぞれの行はレコードを編集するボタンと削除するボタンを表示します。リストの一番下で、ボタンによって新しいレコードを作ることができます。

図14-22 - listビュー内部のインタラクション

listビュー内部のインタラクション

editビューにおいて、一度に編集されるレコードは1つだけであり、定義するアクションのセットは1つだけです。リスト14-31で定義されたeditインタラクションは図14-23のようにレンダリングされます。saveアクションとsave_and_addアクションは現在の編集をレコードに保存します。これらのアクションの違いは、saveアクションは保存したあとで現在のレコード上にeditビューを表示するのに対して、save_adn_addアクションは別のレコードを追加するために空のemptyビューを表示することです。save_and_addアクションは続けざまに多くのレコードを追加するときに非常に便利なショートカットです。deleteアクションの位置に関しては、これはほかのボタンから分離されているので、ユーザーが誤ってクリックすることはありません。

アンダースコア(_)で始まるインタラクション名はsymfonyに対してこれらのアクションに対応するデフォルトのアイコンとアクションを使うように伝えます。administrationジェネレーターは_edit_delete_list_save_save_and_add、と_createを理解します。

図14-23 - editビュー内部のインタラクション

editビュー内部のインタラクション

しかし、リスト14-32のように、アンダースコアでは始まらない名前を指定しなければならない場合において、カスタムインタラクションも追加できます。

リスト14-32 - カスタムインタラクションを定義する

    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { name: Add a comment, action: addComment, icon: backend/addcomment.png }

図14-24で示されるように、リストにおけるそれぞれのアクションはweb/images/addcomment.pngを表示します。これをクリックすることで現在のモジュール内でaddCommentアクションを呼び出すことが行われます。現在のオブジェクトの主キーはリクエストパラメーターに自動的に追加されます。

図14-24 - listビュー内部のカスタムインタラクション

listビュー内部ののカスタムインタラクション

addCommentアクションはリスト14-33のように実装できます。

リスト14-33 - カスタムインタラクションアクション(actions/actions.class.php)

[php]
public function executeAddComment()
{
  $comment = new Comment();
  $comment->setArticleId($this->getRequestParameter('id'));
  $comment->save();

  $this->redirect('comment/edit?id='.$comment->getId());
}

アクションについて最後の一言です: あるカテゴリのためにアクションを完全に抑制したい場合、リスト14-34で示されるように、空のリストを使います。

リスト14-34 - listビューのすべてのアクションを除去する

    list:
      title:          List of Articles
      actions:        {}

フォームのバリデーション

あなたのプロジェクトのcache/ディレクトリのなかで生成された_edit_form.phpテンプレートを見る場合、formフィールドが特別な命名規約を使っていることがわかります。生成されたeditビューにおいて、入力名はアンダースコアの構文のモデルクラスの名前と角かっこで囲まれたフィールド名を連結することで作られます。

たとえば、Articleクラスのためのeditビューがtitleフィールドを持つ場合、テンプレートはリスト14-35のようになりフィールドはarticle[title]として識別されます。

リスト14-35 - 生成された入力名の構文

// generator.yml
generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default
    edit:
      display: [title]

// _edit_form.php テンプレートの結果
<?php echo object_input_tag($article, 'getTitle', array('control_name' => 'article[title]')) ?>

// 結果のHTML
<input type="text" name="article[title]" id="article[title]" value="My Title" />

これは内部のフォームの扱い処理の間において多くの利点があります。しかしながら、10章で説明されたように、バリデーション設定が少し扱いにくくなるので、fields定義において、角かっこの[]を波かっこの{}に変更しなければなりません。しかし、バリデーターに対してフィールド名をパラメーターとして使うとき、生成されたHTMLのコードで現れるものとしてその名前を使うべきです(すなわち波かっこであるが、引用符のあいだで)。生成されたフォームのための特別なバリデーターの構文の詳細についてはリスト14-36を参照してください。

リスト14-36 - administrationで生成されたフォームのためのバリデーターファイルの構文

## フィールドリストのなかの角かっこを波かっこで置き換える
fields:
  article{title}:
    required:
      msg: You must provide a title
    ## バリデーターパラメーターに対して、引用符で囲んだオリジナルのフィールド名を使う
    sfCompareValidator:
      check:        "user[newpassword]"
      compare_error: The password confirmation does not match the password.

クレデンシャルを利用してユーザーのアクションを制限する

特定のadministrationモジュールに対して、利用可能なフィールドとインタラクションはログインしたユーザーのクレデンシャルによって変化します(symfonyのセキュリティ機能の説明に関しては6章を参照)。

適切なクレデンシャルを持つユーザーのみに対して表示されるようにするためにジェネレーター内部のフィールドはcredentialsパラメーターを考慮に入れます。これはlistビューとeditビューに対して機能します。加えて、ジェネレーターはクレデンシャルにしたがってインタラクションも隠すことができます。リスト14-37はこれらの機能のお手本を示しています。

リスト14-37 - クレデンシャルを使う(generator.yml)

## idカラムはadminクレデンシャルを持つユーザーに対してのみ表示される
    list:
      title:          List of Articles
      layout:         tabular
      display:        [id, =title, content, nb_comments]
      fields:
        id:           { credentials: [admin] }

## addcommentインタラクションはadminクレデンシャルを持ったユーザーに制限される
    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { credentials: [admin], name: Add a comment, action: addComment, icon: backend/addcomment.png }

生成されたモジュールのプレゼンテーションを修正する

独自のスタイルシートを適用するだけでなく、デフォルトのテンプレートでオーバーライドすることで、生成されたモジュールのプレゼンテーションが既存のグラフィカルな表にマッチするように、そのモジュールを修正できます。

カスタムスタイルシートを使う

生成されたHTMLコードは構造化された内容を持つので、プレゼンテーションであなたが望むことを大いに行うことができます。

リスト14-38で示されるように、cssパラメーターを生成されたジェネレーターの設定に追加することで、administrationモジュールに対して、デフォルトの代わりにカスタムCSSを定義できます。

リスト14-38 - デフォルトの代わりにカスタムスタイルシートを使う

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default
    css:              mystylesheet

もう一つの方法として、ビュー単位でスタイルをオーバーライドするためにモジュールのview.ymlによって提供されるメカニズムも利用できます。

カスタムヘッダーとフッターを生成する

listビューとeditビューはヘッダーとフッター部分テンプレートを系統的に含むことができます。administrationモジュールのtemplates/ディレクトリのなかにはそのような部分テンプレートは存在しませんが、部分テンプレートを自動的にインクルードするには以下の名前の1つを追加する必要があります:

_list_header.php
_list_footer.php
_edit_header.php
_edit_footer.php

たとえば、カスタムヘッダーをarticle/editビューに追加したい場合、リスト14-39のように_edit_header.phpという名前のファイルを作ります。これは追加の設定なしで動作します。

リスト14-39 - editヘッダー部分テンプレートの例(modules/articles/template/_edit_header.php)

[php]
<?php if ($article->getNbComments() > 0): ?>
  <h2>この記事には<?php echo $article->getNbComments() ?>のコメントがあります</h2>
<?php endif; ?>

edit部分テンプレートはモジュールとして同じ名前を持つ変数を通してつねに現在のオブジェクトにアクセスできるので、$pager変数を通して現在のページャーにアクセスできることに注意してください。

SIDEBAR administrationアクションをカスタムパラメーターで呼び出す

administrationモジュールアクションはlink_to()ヘルパーのquery_string引数を使用してカスタムパラメーターを受けとることができます。たとえば、記事に対するコメントへのリンクを持つprevious_edit_header部分テンプレートを拡張するには、つぎのように書きます:

[php]
This article has <?php echo link_to($article->getNbComments().' comments', 'comment/list', array('query_string' => 'filter=filter&filters%5Barticle_id%5D='.$article->getId())) ?> comments.

このクエリの文字列パラメーターはより読みやすいエンコードされたバージョンです。

[php]
'filter=filter&filters[article_id]='.$article->getId()

これは$articleに関連するコメントだけを表示するためにコメントをフィルタリングします。query_string引数を使うことで、ソートの順番かつ/またはカスタムlistビューを表示するフィルターを指定することもできます。これはカスタムインタラクションに対しても便利です。

テーマをカスタマイズする

カスタム要件を満たすために、モジュールのtemplates/フォルダーのなかでオーバーライドできるフレームワークから継承された別の部分テンプレートが存在します。

ジェネレーターのテンプレートは個別にオーバーライドできる小さな部分に分割可能で、アクションは1つずつ変更できます。

しかしながら、同じ方法でいくつかのモジュールに対してこれらをオーバーライドしたいのであれば、おそらくは再利用可能なテーマを作るべきです。テーマ(theme)はテンプレートとアクションの完全なセットで、generator.ymlの始めでテーマの値に指定された場合、administrationモジュールのなかで使用できます。デフォルトのテーマと一緒に、symfonyは$sf_symfony_data_dir/generator/sfPropelAdmin/default/で定義されたファイルを使います。

テーマのファイルはdata/generator/sfPropelAdmin/[theme_name]/template/ディレクトリ内部の、プロジェクトのツリー構造に設置しなければならず、デフォルトのテーマからファイルをコピーすることで新しいテーマを使い始めることができます($sf_symfony_data_dir/generator/sfPropelAdmin/default/template/ディレクトリに設置されます)。この方法では、テーマのために必要なすべてのファイルはカスタムテーマのなかに存在します:

// [theme_name]/template/templates/内の、部分テンプレート
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php

// [theme_name]/template/actions/actions.class.php内のアクション
processFilters()     // リクエストフィルターを処理する
addFiltersCriteria() // フィルターをCriteriaオブジェクトに追加する
processSort()
addSortCriteria()

テンプレートファイルは実際にはテンプレートのテンプレート(templates of templates)であることに注意してください。すなわち、PHPのファイルはジェネレーターの設定に基づいたテンプレートを生成する特別なユーティリティによって解析されます(これはコンパイレーションフェーズ(compilation phase)と呼ばれます)。生成されたテンプレートが実際にブラウジングしている間に実行されるPHPのコードを含まなければならないので、テンプレートのテンプレートは最初のパスの間にPHPのコードを実行しないようにするために代替構文を使います。リスト14-40はデフォルトのテンプレートのテンプレートの抜粋です。

リスト14-40 - テンプレートのテンプレートの構文

[php]
<?php foreach ($this->getPrimaryKey() as $pk): ?>
[?php echo object_input_hidden_tag($<?php echo $this->getSingularName() ?>,'get<?php echo $pk->getPhpName() ?>') ?]
<?php endforeach; ?>

このリストにおいて、(コンパイル時に)<?によって導入されたPHPのコードは即座に実行され、[?によって導入されたものは実行時のみに実行されますが、テンプレートエンジンは最後には[?タグを<?タグに変換するのでテンプレートの結果はつぎのようになります:

[php]
<?php echo object_input_hidden_tag($article, 'getId') ?>

テンプレートのテンプレートは扱いにくいので、独自のテーマを作りたい場合、もっともお勧めすることはデフォルトのテーマから始め、少しずつ修正し、頻繁にテストすることです。

TIP ジェネレーターのテーマをプラグインのパッケージにすることができるので、再利用性が高くなり、複数のアプリケーションにまたがってデプロイすることが簡単になります。詳細な情報は17章を参照してください。

-

SIDEBAR 独自のジェネレーターを開発する

scaffoldingとadministrationジェネレーターの両方はsymfonyの内部コンポーネントのセットを使います。内部コンポーネントはキャッシュ内部に生成されたアクションとテンプレート、テーマの使用、テンプレートのテンプレートの解析を自動化します。

このことはsymfonyが既存のジェネレーターとは似ているもしくは完全に異なる独自のジェネレーターを作成するためのすべてのツールを提供することを意味します。モジュールの生成はsfGeneratorManagerクラスのgenerate()メソッドで管理されます。たとえば、administrationを生成するために、symfonyは内部でつぎのコードを呼び出します:

[php]
$generator_manager = new sfGeneratorManager();
$data = $generator_manager->generate('sfPropelAdminGenerator', $parameters);

独自のジェネレーターを開発したい場合、sfGeneratorManegerクラスとsfGeneratorクラスのAPIドキュメントを見て、sfAdminGeneratorクラスとsfCRUDGeneratorクラスを例としてください。

まとめ

モジュールをブートストラップするもしくはバックエンドのアプリケーションを自動的に生成するには、基本は洗練されたスキーマとオブジェクトモデルです。scaffoldingのPHPのコードは修正可能ですが、administrationによって生成されたモジュールの多くは設定を通して修正されます。

generator.ymlファイルは生成されたバックエンドのプログラミングにおいて中心的な役割を果たします。このファイルによって内容、機能、listビューとeditビューの見た目を完全にカスタマイズできます。またPHPのコードを一行も書かずに、フィールドラベル、ツールチップ、フィルター、ソートの順番、ページのサイズ、入力タイプ、外部のリレーション、カスタムインタラクション、とクレデンシャルをYAMLで直接管理できます。

administrationジェネレーターが必要な機能をネイティブにサポートしない場合、アクションをオーバーライドする部分テンプレートフィールドと機能は完全な拡張性を提供します。それに加えて、テーマのメカニズムのおかげで、administrationジェネレーターのメカニズムへの適合方法を再利用できます。