The Askeet Tutorial

symfonyアドベントカレンダー2日目:データモデルのセットアップ

Be trained by symfony experts
Dec 10: Paris (1.1 - Francais)
Dec 10: Atlanta (1.1 - English)
Dec 17: Montreal (1.1 - Francais)
Jan 21: Paris (1.1 - Francais)
Feb 18: Paris (1.1 - Francais)
and more...
Askeet_logo_bar

askeet links

WARNING: The SVN source code found in the release_day tags is outdated. Please refer to the current version until each day code is updated.

About

You are currently reading "The Askeet Tutorial" which is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.

Search


powered by google

Chapter Content

おさらい

プロジェクトを公開する

どこから始めるか

データモデル

リレーショナルモデル

schema.xml

オブジェクトモデルの構築

データベース

接続

構築

CRUDを通したテストデータアクセス

ではまた、明日

You are currently browsing "The Askeet Tutorial" in Japanese for the 1.0 version. Switch to another language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.
Translation of this work into another language is explicitly allowed.

おさらい

このチュートリアルの1日目では、symfonyフレームワークをインストールして、新しいアプリケーションと開発環境をセットアップする方法と、バージョンコントロールシステムを使ってコードを安全に管理する方法について学びました。なお、初日に作ったアプリケーションのコードは、以下の askeet SVNリポジトリーから利用可能です:

http://svn.askeet.com/ 

2日目の目的は、最終的に必要となる機能を定義し、データモデルを構築し、コーディングを始めることです。O/Rマッピングをインタラクティブに用いて、アプリケーションの素材や、データベース上のレコードを作成し、検索し、更新することも含みます。

かなり分量が多くなります。それでは 始めましょう。

プロジェクトを公開する

何を知りたいですか?というのは、とても興味深い質問です。世の中には、多くの興味深い質問があります。例えば:

これらの質問は、答えが1つに決まりません。どの答えがもっとも正しいと考えるかは人それぞれでしょう。実際、1つの答えしかない質問は、たいてい全くおもしろくなく(例えば、1 + 1 とか)、ウェブ上で解決することができます。それは不公平です。

askeetは、人々が自分の質問に対する答えを見つけることを助けるために作られたウェブサイトです。しかし、誰がそんな扱いにくい問題に答えるのでしょうか?サイトにアクセスした人全員です。そして、全員が他の人の答えを評価することができるので、人気のある答えは多くの目に触れるようになります。質問の数が増加するに従って、カテゴリとサブカテゴリでそれらを分類するのは不可能になります。そのため、del.icio.usのように質問者は好きなように単語をタグ付けすることができます。タグの人気度はタグバブルの変化で表されることになるでしょう。特定の質問の答えを追跡したい人は、質問のRSS配信を読むこともできます。これらのすべての機能性が洗練され、軽量でなければならないので、新しいページを必要としないインタラクションは、AJAXにするべきです。また、スパムとして報告された質問と答えを管理することや、管理者が奨励する質問を勧めるために、バックエンドが必要です。

次に、こう尋ねるかもしれません:既に同じようなウェブサイトを見たことがなかったかな?…もし、実際にこのようなサイトを見たことがあるなら、それは私たちの失敗ですが、もしfaqtseHowAsk Jeevesや、それに類するウェブサイト、つまり、共同作業的に答えるのではなく、AJAXを使用しておらず、RSSやtagが提供されていないものを考えているなら、Askeetはそれと同類のウェブサイトではありません。ここではWEB2.0アプリケーションを扱っているのです。

askeetについて重要なことは、これがウェブサイトで提供されるだけではなく、誰でもダウンロードして、家や会社のイントラネットにインストールし、カスタマイズして、機能を追加することができるアプリケーションであるということです。ソースコードはオープンソースライセンスでリリースされます。上司はナレッジ・マネジメントシステムを探していませんか?車の修理に関して学んだすべての裏技を記録したくありませんんか?ウェブサイトのよくある質問の部分を発展させたくはありませんか?もう探す必要はありません。Askeetがあります。これは、私たちのクリスマス・プレゼントなのです。

どこから始めるか

では、どのようにsymfonyアプリケーションを始めたらよいでしょう?すべてあなた次第です。XPになれているなら、ストーリーを書き、ゲームを企画し、ペアプログラミングのパートナーを探せばよいのです。また、あなたが UML が好きならすべてのオブジェクト、ステート、インタラクションなどのスケッチと共にウェブサイトの詳細な仕様書を書けばよいのです。

しかし、このチュートリアルは、一般的なアプリケーション開発に関するものではないので、基本的なリレーショナルデータモデルから始めて、機能をひとつずつ加えるつもりです。何も出力しない膨大な書きかけのコードではなく、毎日、使える状態のアプリケーションを作ることを目的とします。理想としては、つけ加えるそれぞれの機能のためのユニットテストを書くべきですが、正直に言ってそれほどの時間はありません。チュートリアルのうち、1日がユニットテストに使われることになるので、読み続けてください。

このプロジェクトでは、整合性制約とトランザクションを利用するため、InnoDBテーブルタイプを持ったMySQLデータベースを使います。データベースのセットアップを避けるため、第一歩としてSQLiteデータベースを使用することもできます。databases.ymlファイルを、ほんの少し書き換える必要がありますが、練習として調べてもらうため、ここではその方法については触れません。

データモデル

リレーショナルモデル

まず、'question'と'answer'テーブルが必要でしょう。'user'テーブルも必要でしょうし、質問に対するユーザーの関心を'interest'テーブルに、人が評価した答えの妥当性を'relevancy'テーブルに格納します。

ユーザは、質問を追加したり、回答の妥当性を評価したり、質問への関心を宣言する時に認証される必要があります。回答を追加する時には、ユーザの認証は必要はないでしょうが、よい回答をしたユーザを区別することができるように、回答とユーザはつねにリンクされるべきでしょう。認証なしで入力された答えは、'Anonymous Coward(匿名の臆病者)'と呼ばれる一般的なユーザの回答として示されます。Entity関係図を見れば、より簡単に分かります:

ERD

各テーブルでcreated_atフィールドを宣言したことに注目してください。Symfonyはこのようなフィールドを認識して、レコードが生成された時間をシステム時間にセットします。updated_atフィールドについても同じです。レコードをアップデートする毎にシステム時間がセットされます。

schema.xml

symfonyに読み込ませるために、リレーショナルモデルをコンフィギュレーションファイルに変換しなければなりません。askeet/config/ディレクトリにあるschema.xmlファイルがそれです。

このファイルを書く方法は2つあります:1つは、手動で書くこと(筆者はこれを好みます)で、もう一つは、すでにあるデータベースから書き出すことです。では、最初の方法を見ていきましょう。まず、デフォルトでインストールされたサンプルの名前を変える必要があります:

$ svn rename config/schema.xml.sample config/schema.xml

Propel ウェブサイトで詳細に説明されていますが、schema.xmlの構文は比較的簡単です: <table>タグが<column><foreign-key>、および <index>タグを含んだ、XMLファイルになっています。一度書いてみれば、すべて書くことができるようになるでしょう。以下は、前に説明したリレーショナルモデルに対応するschema.xmlです:

<?xml version="1.0" encoding="UTF-8"?>
 <database name="propel" defaultIdMethod="native" noxsd="true">
   <table name="ask_question" phpName="Question">
     <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
     <column name="user_id" type="integer" />
     <foreign-key foreignTable="ask_user">
       <reference local="user_id" foreign="id"/>
     </foreign-key>
     <column name="title" type="longvarchar" />
     <column name="body" type="longvarchar" />
     <column name="created_at" type="timestamp" />
     <column name="updated_at" type="timestamp" />
   </table>
 
   <table name="ask_answer" phpName="Answer">
     <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
     <column name="question_id" type="integer" />
     <foreign-key foreignTable="ask_question">
       <reference local="question_id" foreign="id"/>
     </foreign-key>
     <column name="user_id" type="integer" />
     <foreign-key foreignTable="ask_user">
       <reference local="user_id" foreign="id"/>
     </foreign-key>
     <column name="body" type="longvarchar" />
     <column name="created_at" type="timestamp" />
   </table>
 
   <table name="ask_user" phpName="User">
     <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
     <column name="nickname" type="varchar" size="50" />
     <column name="first_name" type="varchar" size="100" />
     <column name="last_name" type="varchar" size="100" />
     <column name="created_at" type="timestamp" />
   </table>
 
   <table name="ask_interest" phpName="Interest">
     <column name="question_id" type="integer" primaryKey="true" />
     <foreign-key foreignTable="ask_question">
       <reference local="question_id" foreign="id"/>
     </foreign-key>
     <column name="user_id" type="integer" primaryKey="true" />
     <foreign-key foreignTable="ask_user">
       <reference local="user_id" foreign="id"/>
     </foreign-key>
     <column name="created_at" type="timestamp" />
   </table>
 
   <table name="ask_relevancy" phpName="Relevancy">
     <column name="answer_id" type="integer" primaryKey="true" />
     <foreign-key foreignTable="ask_answer">
       <reference local="answer_id" foreign="id"/>
     </foreign-key>
     <column name="user_id" type="integer" primaryKey="true" />
     <foreign-key foreignTable="ask_user">
       <reference local="user_id" foreign="id"/>
     </foreign-key>
     <column name="score" type="integer" />
     <column name="created_at" type="timestamp" />
   </table>
 
 </database>
 

実際のデータベース名にかかわらず、このファイルの中ではデータベース名がpropelにセットされていることに注意してください。このパラメータは、Propelレイヤーをsymfonyのフレームワークに接続するために使用されます。データベースの実際の名前はdatabases.yml構成ファイルで定義します(以下を見てください)。

すでに既存のデータベースを持っている場合、schema.xmlを作成する別の方法もあります。グラフィカルなデータベースデザインツールになじみがある場合は、生成されたMySQLデータベースからスキーマを作るのがよいでしょう。それを行う前に、askeet/config/ディレクトリに位置する propel.iniファイルを編集して、データベースへの接続設定を入力する必要があります:

propel.database.url = mysql://username:password@localhost/databasename

usernamepasswordlocalhostdatabasenameはデータベースの実際の接続設定に置き換えてください。データベースからschema.xmlを作成するため、propel-build-schemaコマンドをaskeet/ディレクトリにて実行してください:

$ symfony propel-build-schema

注意: いくつかのツールでは、グラフィカルな環境でデータベースを構築でき (例:Fabforce's Dbdesigner)、その上、schema.xmlを直接生成することができます( DB Designer 4 TO Propel Schema Converterを利用)。

オブジェクトモデルの構築

InnoDBエンジンを使用するためには、askeet/config/ディレクトリのpropel.iniファイルに一行追加しなければなりません:

propel.mysql.tableType = InnoDB

schema.xmlがいったん構築されると、リレーショナルモデルに基づいたオブジェクト・モデルを作成することができます。symfonyでは、O/Rマッピングは、Propelによって制御されますが、symfonyコマンドにカプセル化されます:

$ symfony propel-build-model

このコマンドは、askeetプロジェクトのルートディレクトリから呼びだす必要があり、標準のアクセサ( ->get()、->set()メソッド)とともに、schemaで定義されたテーブルに対応するクラスを生成します。生成されたコードはaskeet/lib/model/om/ディレクトリにあります。1テーブルあたり2つのクラスがあることについては、symfony bookの model chapterを参照してください。これらのクラスは、build-modelを実行するたびに上書きされることになりますが、これはこのプロジェクトでは何回も実行されます。したがって、モデルオブジェクトにメソッドを加える必要がある場合、askeet/lib/model/ディレクトリにあるクラスを変更しなければなりません。これらのクラスは /omディレクトリ下のクラスを継承しています。

データベース

接続

ここまでの間に、symfonyはデータベースのオブジェクト・モデルを持つようになりました。ここで、プロジェクトをMySQLデータベースに関連づけましょう。まず最初に、MySQLにデータベースを作成しなければなりません:

$ mysqladmin -u youruser -p create askeet

askeet/config/databases.ymlコンフィギュレーションファイルを開いてください。初めての場合、symfonyコンフィギュレーションファイルが YAMLで書かれていることに気づくでしょう。構文は非常に簡単ですが、YAMLファイルには1つの重要な決まり事があります。タブを決して使用せず、必ずスペースを使用することです。それがわかれば、ファイルを編集し、all:カテゴリの下にあるデータベースの接続設定を入力する準備ができています:

all:
  propel:
    class:          sfPropelDatabase
    param:
      phptype:  mysql
      host:     localhost
      database: askeet
      username: youruser
      password: yourpasswd

symfonyコンフィギュレーションとYAMLファイルに関してさらに知りたい場合、symfony bookのconfiguration in practice chapterを読んでください。

構築

手で schema.xmlファイルを書かなかった場合は、データベースに、対応するテーブルをすでに持っている可能性があります。その場合、この部分をとばすことができます。

キーボードファンにとって、驚きのニュースがあります。MySQLデータベースに、テーブルとコラムを作成する必要はありません。schema.xmlですでに一度行っているので、symfonyがあなたの代わりにSQLステートメントを構築してくれます。

$ symfony propel-build-sql

このコマンドはaskeet/data/sql/ディレクトリに lib.model.schema.sqlを作成します。MySQLのSQLコマンドとして使用してください:

$ mysql -u youruser -p askeet < data/sql/lib.model.schema.sql

CRUDを通したテストデータアクセス

これまで行ったことがきちんと役に立っていることを確認するのは、うれしいものです。これまで、ブラウザを少しも使いませんでしたが、私たちはウェブアプリケーションを組立てることになっています… それではここで、基本的なsymfonyテンプレートのセットとアクションを作成し、'question'テーブルのデータを操作しましょう。これで、いくつかの質問を作成して、それらを表示することができるようになります。

askeet/ディレクトリで、以下のように入力してください:

$ symfony propel-generate-crud frontend question Question

これで、frontendアプリケーションのquestionモジュールに、QuestionPropelのオブジェクト・モデルに基づいて、基本的なCreate,Retrieve,Update,Deleteのアクション(CRUDと略される)を持った足場を作成されます。なお、勘違いしないでしないでください。足場は完成されたアプリケーションではなく、新機能をつけ、ビジネスルールを付け加え、ルック&フィールをカスタマイズすることのできる基本構造です。

CRUDジェネレータによって作成されたアクションのリストは以下の通りです。

アクション名 説明
list テーブルの全レコードを表示します
index リストへ転送する
show 与えられたレコードのすべてのフィールドを示します
edit 新しいレコードを作成するか、既存のものを編集するためのフォームを表示します
update 要求で与えられたパラメタに従ってレコードを変更し、showに転送します
delete 与えられたレコードをテーブルから削除します

アクションについて、詳しくは、symfony bookの scaffolding chapterを参照してください。

askeet/apps/frontend/modules/ディレクトリでは、新しいquestionモジュールに注意して、ソースを閲覧してください。

自動的に読み込む必要がある新しいクラスを作るときは、コンフィグキャッシュをクリアする(autoloadingキャッシュを再読み込みする)ことを忘れないようにしてください。

$ symfony cc frontend config

以下のアドレスで、オンラインでテストすることができます。

http://askeet/question

Create a new record List all records

遊んでみてください。いくつかの質問を加え、編集し、表示し、削除してみてください。作動した場合、オブジェクト・モデルが正しく、データベースとの接続が正しく、データベースのリレーショナルモデルとsymfonyのオブジェクト・モデルの間のマッピングが正しいことを意味します。いい機能テストになります。

ではまた、明日

PHPを一行も書きませんでしたが、使用できる基本的なアプリケーションが手に入りました。2日目としては悪くはありません。明日、質問のリストを表示し、訪問者を歓迎するホームページを持つためにコードを書き始めましょう。また、バッチプロセスを使用してテストデータをデータベースに追加し、モデルを拡張する方法を学びましょう。

アプリケーションが何をするかわかったことで、付加的な機能を思いついたかもしれませんん。askeetメーリングリストに自由に提案してください。評判のいいアイディアはsymfonyアドベントカレンダーの21日目に付け加えられるでしょう。

遠慮なく、以下のアドレスで今日のチュートリアル(タグ release_day_2)のソースを閲覧してください。

http://svn.askeet.com/tags/release_day_2
symfony アドイベント カレンダー 3日目: MVC構造へダイブ »
« symfony アドベントカレンダー 1日目: プロジェクトを立ち上げる

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the user mailing-list or to the forum.