Development

Documentation/ja_JP/book/1.0/02-Exploring-Symfony-s-CodeExploring

You must first sign up to be able to contribute.

Version 10 (modified by river.bright, 11 years ago)
--

第2章 - symfonyのコードを覗いてみる

一見、symfony のコードはとてつもなくとっつきにくいと思われるかもしれません。なぜなら大量のディレクトリ、スクリプトが含まれており、ファイルには PHP のクラス、HTML, さらにはその両方がが入り混じっているからです。また、アプリケーションフォルダのどこにもないクラスを参照している部分にも気づくでしょう、そしてそのディレクトリの階層は6階層という深いものになっています。しかし、複雑そうに見えるこれら背後にある理由を一度理解してしまえば、symfony のアプリケーション構造を他の構造にしないということが自然に感じられるでしょう。この章ではこのとっつきにくい感覚について取り払うために説明します。

MVCパターン

symfony はMVCアーキテクチャーとして知られている古典的ウェブデザインパターンに基づいており、このアーキテクチャーは3つのレベルで構成されています。

  • モデルはアプリケーションの操作部分についての情報、つまりビジネスロジックを担当します。
  • ビューはモデルをユーザーと対話するのに合ったウェブページになるように画面表示処理を行います。
  • コントローラーは適切にユーザーアクションに応じて、モデルまたはビュー上で変化を実施します

Figure 2-1 illustrates the MVC pattern.

MVC構造はビジネスロジック(model)とプレゼンテーション部分(view)を分離し、結果としてより保全性に優れたものになります。たとえば、あなたのアプリケーションがウェブブラウザと携帯端末の両方で動作させる必要があるなら、新しいビューを作成するだけです、つまり、元々のコントローラーとモデルはそのままで大丈夫なのです。コントローラーによってリクエストに使用されるプロトコルの詳細(HTTP、コンソールモード、メール等)をモデルとビューから隠すことができます。そして、モデルはデータのロジックを抽象化します、このデータとはビューとアクションに依存しており、たとえば、アプリケーションで利用されるデータベースのようなものです。

Figure 2-1 - The MVC pattern

MVC レイヤー処理

MVCによる利点を理解するために、どのように基本的なPHPアプリケーションをMVC構造化されたアプリケーションに適用するかを見てみましょう。ウェブログアプリケーションの投稿された記事のリストが例にぴったりでしょう。

1ファイルでのプログラミング(Flat Programming)

1つのPHPファイルで、データベースのリストを表示するスクリプトは2-1のようになるでしょう。

Listing 2-1 - A Flat Script

[php]
<?php

// データベースに接続し、データベースを選択します
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// SQLクエリを実行します
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// HTMLで結果を表示します
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>

<?php

// データベース接続を閉じます
mysql_close($link);

?>

手早く書け、実行速度も速く、メンテナンスも起こりえないでしょう。そして次のような大きな問題がこのコードには含まれています。

  • エラーチェックが行われていない(データベース接続が失敗したらどうする?)
  • HTMLとPHPコードが混在しており、さらにはHTMLやPHPが交互に出現しています
  • このコードはMySQLデータベースに深く依存している

プレゼンテーション部分を分離する

2-1のように echoprintf を呼び出すことによって可読性が低くなります。プレゼンテーション部分を改善するためにHTMLコードを改修することは現在の構文では困難です。それで、2つの部分に分けます。1つはコントローラースクリプトでビジネスロジック部分である素のPHPコードであり、2-2のようになります。

Listing 2-2 - index.php にあるコントローラー部分

[php] <?php

// 接続し、データベースを選択します
// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// SQLクエリーを実行します
$result = mysql_query('SELECT date, title FROM post', $link);

// ビューのために配列を作成します
$posts = array();
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
   $posts[] = $row;
}

// 接続を閉じます
mysql_close($link);

// ビューをrequireします
require('view.php');

?>

HTMLコードは、ここでPHP構文のようなテンプレートを含んでおり、ビュースクリプトに保存されます。それが次のような2-3になります。

Listing 2-3 - view.phpがビュー部分です。

[php]
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

ビューが十分綺麗かどうかを決めるよい経験則は最小限のPHPコードで構成されているかどうかであり、PHPをあまり知らないデザイナーによって理解されるためです。もっとも一般的なビューにおける記述は echo や if/endif や foreach/endforeach などでしょう。そしてHTMLタグをechoするようなPHPコードは存在させるべきではありません。

全てのロジックはコントローラースクリプトに移動させます。そして、素のPHPコードだけを含んでおり、HTMLはありません。実際、同じコントローラーは全体が異なるプレゼンテーションのために再利用できるということを考慮しなければなりません。それはPDFファイルやXML構造であったりするかもしれません。

データ操作部分を分離する

コントローラースクリプトの大部分はデータ操作に費やされます。もし別のコントローラーのために投稿されたリストが必要になるような、ウェブログの記事のRSSフィードを出力するような場合はどうしますか?コードが重複するのを避けるために、データベースクエリーを1箇所にまとめたい場合にどうしますか?postテーブルをweblog_postテーブルと名前を変更するためにモデルを変更しようとしたらどうしますか?これら全てが行えるように、データ操作に関するコードはコントローラーから取り除き、2-4に示すようなモデルと呼ばれる別のスクリプト内に置くべきです。

Listing 2-4 - model.phpにあるモデル部分

[php]
<?php

function getAllPosts()
{
  // 接続し、データベースを選択します
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // SQLクエリーを実行します
  $result = mysql_query('SELECT date, title FROM post', $link);

  // 配列を作成します
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // 接続を閉じます
  mysql_close($link);

  return $posts;
}

?>

修正されたコントローラーは2-5のようになります

Listing 2-5 - 修正されたindex.phpのコントローラー部分

[php]
<?php

// モデルをrequireします
require_once('model.php');

// 記事のリストを取得します
$posts = getAllPosts();

// ビューをrequireします
require('view.php');

?>

コントローラーの可読性がよくなりました。コントローラーがすることはモデルからデータを取得しビューに渡すことだけです。より複雑なアプリケーションでは、コントローラーはリクエスト、ユーザーのセッション、認証などについても扱うでしょう。モデルの関数命として明確な名前をつけることがコントローラー内でコメントを減らすことになるでしょう。

モデルスクリプトはデータアクセス部分に費やされ、結果として構造的にしっかりしたものになるでしょう。全てのパラメーターは直接モデルによって参照されることがないコントローラーによって与えられる(リクエストパラメーターなどのような)データ層に依存しません。モデル関数は簡単に別のコントローラーで再利用できます。

MVCでのレイヤー分割

MVC構造の主要な部分はコードを3つのレイヤーに分割することなので、自然とそうなりました。データロジックコードはモデル内に、表示部分のコードはビュー内に、そしてアプリケーションロジックはコントローラー内に配置されています。

別のデザインパターンを適用することでコードをより明確にすることができます。モデル、ビュー、コントローラーレイヤーをより細分化することができます。

データベース抽象化

モデルレイヤーはデータアクセスレイヤーとデータベース抽象レイヤーの2つに分けることができます。そのため、データアクセス関数はデータベースに依存しているクエリ文を使う必要がありません。しかし、その依存するクエリ文を使っていえる関数を呼び出す必要があるかもしれません。データベースシステムを後で変更するなら、データベース抽象レイヤーを更新するだけです。

MySQLに特化したデータアクセスレイヤーの例が2-6にあります。そしてサンプルのデータベース抽象化レイヤーはその次の2-7にあります。

Listing 2-6 - モデルにおけるMySQLに特化したデータベース抽象部分

[php]
<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

Listing 2-7 - モデルにおけるデータアクセス部分

[php]
function getAllPosts()
{
  // Connecting to database
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // Performing SQL query
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // Filling up the array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Closing connection
  close_connection($link);

  return $posts;
}

?>

データアクセスレイヤー内にある関数がデータベースエンジンに依存していないことを確認することができます。更に、データベース抽象レイヤーで作成された関数はデータベースにアクセスする必要がある多くの他のモデル関数で再利用できます。

注意 2-6 や 2-7 の例は満足できるものではありませんし、完全なデータベース抽象化(データベースに依存しないクエリ作成機能を通してSQLコードを抽象化したり、全ての関数をクラスの中に移動させるなど)という点においてまだ作業が残っています。しかし、この本の目的は手書きで完全なコードをどうやって書くかということではなく、第8章でsymfonyそのものが抽象化を上手く行っていることがわかるでしょう。

ビューエレメント

ビューレイヤーもコード分離から得ることができる利点です。ウェブページはアプリケーションを通して一貫性のある要素(エレメント)を含むものです。つまり、ページヘッダー、グラフィカルなレイアウト、フッターや共通のナビゲーションなどがそうです。そしてこれらはページが変化する部分の中にだけ存在します。そういうわけで、ビューはレイアウトとテンプレートに分離されています。レイアウトはアプリケーションや複数ページのあつまりで一般的に共通部分です。テンプレートはコントローラーによって利用された変数が配置されます。これらのコンポーネントが動くようにロジックが必要になったり、ロジックレイヤーは名前をviewにしておくでしょう。これらの原理によると、2-3のようなビューの部分を3つの 2-8, 2-9, 2-10に示すような部分に分離することができます。

Listing 2-8 - The Template Part of the View, in mytemplate.php

[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-9 - The View Logic Part of the View

[php]
<?php

$title = 'List of Posts';
$content = include('mytemplate.php');

?>

Listing 2-10 - The Layout Part of the View

[php]
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

アクションとフロントコントローラー

コントローラーは上記サンプルではそれほど大したことはしませんが、実際のアプリケーションにおいては、コントローラーは非常に沢山の仕事をこなします。この仕事の大部分はアプリケーションのコントローラー全てに共通のことです。その共通のタスクはリクエスト操作、セキュリティ操作、アプリケーションの設定ファイルの読み込みや同じようないつも行う作業などです。こういうわけで、コントローラーはフロントコントローラーという、アプリケーション全体で唯一なものと、アクションという特定のページに対してのコントローラーコードだけを含むものにしばしば分割されます。

フロントコントローラーのもっとも大きな利点の1つはアプリケーション全体でエントリーポイントが唯一であるということです。もしアプリケーションを閉鎖するなら、フロントコントローラースクリプトさえ編集すればよいのです。フロントコントローラーがないアプリケーションでは、各戸別のコントローラーを無効にする必要があります。

オブジェクト指向

全てのこれまで例は手続きプログラミングを使っています。現代言語のOOPの可能性によって、プログラミングをより簡単なものにすることができます。なぜなら、オブジェクトはロジックを閉じ込めたり、他のものから継承したり、命名規則を綺麗な状態にしておくことができます。

オブジェクト指向でない言語でMVC構造を実装すると、ネームスペースや重複コードの問題が発生し、全体の可読性も低くなります。

オブジェクト指向によって、開発者はビューオブジェクト、コントローラーオブジェクトそしてモデルオブジェクトとして扱うことができ、前述のような全ての機能をメソッドの中に移動させることができます。MVC構造のためには絶対必要なことです。

TIP オブジェクト指向でウェブアプリケーションのためのデザインパターンについて更に知りたいなら、Martin Fowler著のPatterns of Enterprise Application Architecture(Addison-Wesley, ISBN: 0-32112-742-0)を読んでください。Fowlerの本のサンプルコードはJava、C#などで書かれていますが、まだPHP開発者には読み易いものでしょう。

symfonyの MVC 実装

もうちょっと待ってください。ウェブログで記事を一覧表示している1つのページのために、どれだけ多くのコンポーネントが必要とされるでしょう?図2-2のように、次のような部分があります。

  • Model レイヤー
    • データベース抽象化
    • データアクセス
  • View レイヤー
    • View
    • レンプレート
    • レイアウト
  • Controller レイヤー
    • フロントコントローラー
    • アクション

7つのスクリプト-- 新しいページを作成するたびに全体では多くのファイルを開き、修正しなければなりません! しかしながら、symfonyによって簡単に行えます。もっともベストなMVC構造を考慮する一方で、symfonyはアプリケーション開発が早く苦痛でないようにする方法で実装します。

まず最初に、フロントコントローラーとレイアウトはアプリケーションでの全てのアクションに共通です。複数のコントローラーとレイアウトを持つこともできますが、それぞれに1つだけ必要です。フロントコントローラーは純粋なMVCロジックコンポーネントで、1度も書く必要はありません。なぜならsymfonyがあなたに代わってフロントコントローラーを生成してくれるからです

もう一方の良い知らせはモデルレイヤーのクラスも、あなたのデータ構造に基づいて自動的に生成されるということです。これはPropelライブラリの仕事であり、Propelライブラリクラスはスケルトンクラスとコード生成機能を提供しています。Propelが外部キー制約やデータ項目を見つけると、特別なアクセッサーとデータ操作をまるで1個のケーキのように扱うことができる関数を提供します。データベース抽象化はあなたの見えるところにはないでしょう。なぜなら、全く別のコンポーネントによって扱われるからです。そして扱っているのがCreoleになります。そのため、あなたのデータベースエンジンをあるタイミングで変更することを決めるなら、コードを書き直すことが全く必要ありません。1つの設定パラメーターを変更するだけです。

最後は、ビューロジックは単純な設定ファイルによって簡単に翻訳することができることです。プログラミングは必要ありません。

Figure 2-2 - symfony ワークフロー

私たちの例において記載された記事のリストはsymfonyで動作するためにはたった3つのファイルだけが必要ということを意味しており、それは2-11、2-12そして2-13のようになります。

Listing 2-11 - list Action, in myproject/apps/myapp/modules/weblog/actions/actions.class.php

[php]
<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}

?>

Listing 2-12 - list Template, in myproject/apps/myapp/modules/weblog/templates/listSuccess.php

[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-13 - list View, in myproject/apps/myapp/modules/weblog/config/view.yml

listSuccess:
  metas: { title: List of Posts }

更に、レイアウトを定義する必要があります。2-14のようになりますが、何度も再利用できるものです。

Listing 2-14 - Layout, in myproject/apps/myapp/templates/layout.php

[php]
<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>

これで必要なもの全てです。これが2-1で最初に示した単一ファイルでのスクリプトと同じように表示するために必要な精密なコードです。残り(上手く動作するための全てのコンポーネント作成)はsymfonyによって操作されます。行数を数えるなら、symfonyでMVC構造で記事のリストを作成することが単一ファイルで作成するよりもコーディング量が少ないということがわかるでしょう。またそれにもかかわらず、symfonyによって莫大な利点、特に明確なコード構造、再利用性、柔軟性など多くのことが与えられるということです。そしてボーナスとして、XHTMLに適していて、デバッグが容易、設定が簡単、データベース抽象化、スマートなURLルーティング処理、複数の環境対応、そして多くの開発ツールが付いてきます。

symfony のコアクラス

symfonyでのMVCによる実装はこの本において良く見るであろう複数のクラスを利用しています。

  • sfController はコントローラークラスです。リクエストをデコード処理しアクションに導きます
  • sfRequest は全てのリクエスト要素(パラメーター、クッキー、ヘッダーなど)を貯めています
  • sfResponse はレスポンスヘッダーとコンテンツを含んでいます。最終的にHTMLに変換しユーザーに送るためのオブジェクトです。
  • (sfContext::getInstance()によって取得できる)contextシングルトンは全てのコアオブジェクトと現在の設定内容を保持しており、どこからでもアクセスできます。

このことについては第6章でさらに学ぶことができます。

見ればわかるように、全てのsymfonyクラスは sfという接頭辞を使っており、テンプレート内のsymfonyのコア変数も同様です。これはあなた自身のクラスや変数と衝突するのを避けるためであり、コアフレームワーククラスを馴染みやすいようにし、認識しやすいようにします。

注意 symfonyで利用されているコーディング標準に、大文字で始まるキャメルケースがクラス名と変数名に標準となっています。しかし、2つの例外が存在します。それがsfで始まるsymfonyのコアクラスであり、小文字で始まります。そして、テンプレートで利用される変数名はアンダースコアによって区切る構文になっています。

Code Organization

Now that you know the different components of a symfony application, you're probably wondering how they are organized. Symfony organizes code in a project structure and puts the project files into a standard tree structure.

Project Structure: Applications, Modules, and Actions

In symfony, a project is a set of services and operations available under a given domain name, sharing the same object model.

Inside a project, the operations are grouped logically into applications. An application can normally run independently of the other applications of the same project. In most cases, a project will contain two applications: one for the front-office and one for the back-office, sharing the same database. But you can also have one project containing many mini-sites, with each site as a different application. Note that hyperlinks between applications must be in the absolute form.

Each application is a set of one or more modules. A module usually represents a page or a group of pages with a similar purpose. For example, you might have the modules home, articles, help, shoppingCart, account, and so on.

Modules hold actions, which represent the various actions that can be done in a module. For example, a shoppingCart module can have add, show, and update actions. Generally, actions can be described by a verb. Dealing with actions is almost like dealing with pages in a classic web application, although two actions can result in the same page (for instance, adding a comment to a post in a weblog will redisplay the post with the new comment).

TIP If this represents too many levels for a beginning project, it is very easy to group all actions into one single module, so that the file structure can be kept simple. When the application gets more complex, it will be time to organize actions into separate modules. As mentioned in Chapter 1, rewriting code to improve its structure or readability (but preserving its behavior) is called refactoring, and you will do this a lot when applying RAD principles.

Figure 2-3 shows a sample code organization for a weblog project, in a project/ application/module/action structure. But be aware that the actual file tree structure of the project will differ from the setup shown in the figure.

Figure 2-3 - Example of code organization

File Tree Structure

All web projects generally share the same types of contents, such as the following:

  • A database, such as MySQL or PostgreSQL
  • Static files (HTML, images, JavaScript files, style sheets, and so on)
  • Files uploaded by the site users and administrators
  • PHP classes and libraries
  • Foreign libraries (third-party scripts)
  • Batch files (scripts to be launched by a command line or via a cron table)
  • Log files (traces written by the application and/or the server)
  • Configuration files

Symfony provides a standard file tree structure to organize all these contents in a logical way, consistent with the architecture choices (MVC pattern and project/application/module grouping). This is the tree structure that is automatically created when initializing every project, application, or module. Of course, you can customize it completely, to reorganize the files and directories at your convenience or to match your client's requirements.

Root Tree Structure

These are the directories found at the root of a symfony project:

apps/
  frontend/
  backend/
batch/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

Table 2-1 - describes the contents of these directories.

Table 2-1. Root Directories Directory Description

apps/

Contains one directory for each application of the project (typically, frontend and backend for the front and back office).

batch/

Contains PHP scripts called from a command line or a scheduler, to run batch processes.

cache/

Contains the cached version of the configuration, and (if you activate it) the cache version of the actions and templates of the project. The cache mechanism (detailed in Chapter 12) uses these files to speed up the answer to web requests. Each application will have a subdirectory here, containing preprocessed PHP and HTML files.

config/

Holds the general configuration of the project.

data/

Here, you can store the data files of the project, like a database schema, a SQL file that creates tables, or even a SQLite database file.

doc/

Stores the project documentation, including your own documents and the documentation generated by PHPdoc.

lib/

Dedicated to foreign classes or libraries. Here, you can add the code that needs to be shared among your applications. The model/ subdirectory stores the object model of the project (described in Chapter 8).

log/

Stores the applicable log files generated directly by symfony. It can also contain web server log files, database log files, or log files from any part of the project. Symfony creates one log file per application and per environment (log files are discussed in Chapter 16).

plugins/

Stores the plug-ins installed in the application (plug-ins are discussed in Chapter 17).

test/

Contains unit and functional tests written in PHP and compatible with the symfony testing framework (discussed in Chapter 15). During the project setup, symfony automatically adds some stubs with a few basic tests.

web/

The root for the web server. The only files accessible from the Internet are the ones located in this directory.

Application Tree Structure

The tree structure of all application directories is the same:

apps/
  [application name]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
      error.php
      error.txtdelete

Table 2-2 - describes the application subdirectories.

Table 2-2. Application Subdirectories Directory Description

config/

Holds a hefty set of YAML configuration files. This is where most of the application configuration is, apart from the default parameters that can be found in the framework itself. Note that the default parameters can still be overridden here if needed. You'll learn more about application configuration in the Chapter 5.

i18n/

Contains files used for the internationalization of the application--mostly interface translation files (Chapter 13 deals with internationalization). You can bypass this directory if you choose to use a database for internationalization.

lib/

Contains classes and libraries that are specific to the application.

modules/

Stores all the modules that contain the features of the application.

templates/

Lists the global templates of the application--the ones that are shared by all modules. By default, it contains a layout.php file, which is the main layout in which the module templates are inserted.

NOTE The i18n/, lib/, and modules/ directories are empty for a new application.

The classes of an application are not able to access methods or attributes in other applications of the same project. Also note that hyperlinks between two applications of the same project must be in absolute form. You need to keep this last constraint in mind during initialization, when you choose how to divide your project into applications.

Module Tree Structure

Each application contains one or more modules. Each module has its own subdirectory in the modules directory, and the name of this directory is chosen during the setup.

This is the typical tree structure of a module:

apps/
  [application name]/
    modules/
      [module name]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

Table 2-3 - describes the module subdirectories.

Table 2-3. Module Subdirectories Directory Description

actions/

Generally contains a single class file named actions.class.php, in which you can store all the actions of the module. You can also write different actions of a module in separate files.

config/

Can contain custom configuration files with local parameters for the module.

lib/

Stores classes and libraries specific to the module.

templates/

Contains the templates corresponding to the actions of the module. A default template, called indexSuccess.php, is created during module setup.

validate/

Dedicated to configuration files used for form validation (discussed in Chapter 10).

NOTE The config/, lib/, and validate/ directories are empty for a new module.

Web Tree Structure

There are very few constraints for the web directory, which is the directory of publicly accessible files. Following a few basic naming conventions will provide default behaviors and useful shortcuts in the templates. Here is an example of a web directory structure:

web/
  css/
  images/
  js/
  uploads/

Conventionally, the static files are distributed in the directories listed in Table 2-4.

Table 2-4. Typical Web Subdirectories Directory Description

css/

Contains style sheets with a .css extension.

images/

Contains images with a .jpg, .png, or .gif format.

js/

Holds JavaScript files with a .js extension.

uploads/

Must contain the files uploaded by the users. Even though the directory usually contains images, it is distinct from the images directory so that the synchronization of the development and production servers does not affect the uploaded images.

NOTE Even though it is highly recommended that you maintain the default tree structure, it is possible to modify it for specific needs, such as to allow a project to run in a server with different tree structure rules and coding conventions. Refer to Chapter 19 for more information about modifying the file tree structure.

Common Instruments

A few techniques are used repeatedly in symfony, and you will meet them quite often in this book and in your own projects. These include parameter holders, constants, and class autoloading.

Parameter Holders

Many of the symfony classes contain a parameter holder. It is a convenient way to encapsulate attributes with clean getter and setter methods. For instance, the sfResponse class holds a parameter holder that you can retrieve by calling the getParameterHolder() method. Each parameter holder stores data the same way, as illustrated in Listing 2-15.

Listing 2-15 - Using the sfResponse Parameter Holder

[php]
$response->getParameterHolder()->set('foo', 'bar');
echo $response->getParameterHolder()->get('foo');
 => 'bar'

Most of the classes using a parameter holder provide proxy methods to shorten the code needed for get/set operations. This is the case for the sfResponse object, so you can do the same as in Listing 2-15 with the code of Listing 2-16.

Listing 2-16 - Using the sfResponse Parameter Holder Proxy Methods

[php]
$response->setParameter('foo', 'bar');
echo $response->getParameter('foo');
 => 'bar'

The parameter holder getter accepts a default value as a second argument. This provides a useful fallback mechanism that is much more concise than possible with a conditional statement. See Listing 2-17 for an example.

Listing 2-17 - Using the Attribute Holder Getter's Default Value

[php]
// The 'foobar' parameter is not defined, so the getter returns an empty value
echo $response->getParameter('foobar');
 => null

// A default value can be used by putting the getter in a condition
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default

// But it is much faster to use the second getter argument for that
echo $response->getParameter('foobar', 'default');
 => default

The parameter holders even support namespaces. If you specify a third argument to a setter or a getter, it is used as a namespace, and the parameter will be defined only within that namespace. Listing 2-18 shows an example.

Listing 2-18 - Using the sfResponse Parameter Holder Namespace

[php]
$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
 => 'bar2'

Of course, you can add a parameter holder to your own classes to take advantage of its syntax facilities. Listing 2-19 shows how to define a class with a parameter holder.

Listing 2-19 - Adding a Parameter Holder to a Class

[php]
class MyClass
{
  protected $parameter_holder = null;

  public function initialize ($parameters = array())
  {
    $this->parameter_holder = new sfParameterHolder();
    $this->parameter_holder->add($parameters);
  }

  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
}

Constants

Surprisingly, you will find very few constants in symfony. This is because constants have a major drawback in PHP: you can't change their value once they are defined. So symfony uses its own configuration object, called sfConfig, which replaces constants. It provides static methods to access parameters from everywhere. Listing 2-20 demonstrates the use of sfConfig class methods.

Listing 2-20 - Using the sfConfig Class Methods Instead of Constants

[php]
// Instead of PHP constants,
define('SF_FOO', 'bar');
echo SF_FOO;
// Symfony uses the sfConfig object
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

The sfConfig methods support default values, and you can call the sfConfig::set() method more than once on the same parameter to change its value. Chapter 5 discusses sfConfig methods in more detail.

Class Autoloading

Classically, when you use a class method or create an object in PHP, you need to include the class definition first.

[php]
include 'classes/MyClass.php';
$myObject = new MyClass();

But on large projects with many classes and a deep directory structure, keeping track of all the class files to include and their paths takes a lot of time. By providing an __autoload() function (or a spl_autoload_register() function), symfony makes include statements unnecessary, and you can write directly:

[php]
$myObject = new MyClass();

Symfony will then look for a MyClass definition in all files ending with php in one of the project's lib/ directories. If the class definition is found, it will be included automatically.

So if you store all your classes in lib/ directories, you don't need to include classes anymore. That's why the symfony projects usually do not contain any include or require statements.

NOTE For better performance, the symfony autoloading scans a list of directories (defined in an internal configuration file) during the first request. It then registers all the classes these directories contain and stores the class/file correspondence in a PHP file as an associative array. That way, future requests don't need to do the directory scan anymore. This is why you need to clear the cache every time you add or move a class file in your project by calling the symfony clear-cache command. You will learn more about the cache in Chapter 12, and about the autoloading configuration in Chapter 19.

Summary

Using an MVC framework forces you to divide and organize your code according to the framework conventions. Presentation code goes to the view, data manipulation code goes to the model, and the request manipulation logic goes to the controller. It makes the application of the MVC pattern both very helpful and quite restricting.

Symfony is an MVC framework written in PHP 5. Its structure is designed to get the best of the MVC pattern, but with great ease of use. Thanks to its versatility and configurability, symfony is suitable for all web application projects.

Now that you understand the underlying theory behind symfony, you are almost ready to develop your first application. But before that, you need a symfony installation up and running on your development server.


目次