站內搜尋:Yahoo搜尋如果沒有給完整的網址,請在站內再搜尋一次!

2011-10-21

symfony Jobeet學習紀錄--Jobeet網站的資料模型(The Data Model)--(03/24)

Symfony 的練習範例Jobeet : http://www.symfony-project.org/jobeet/1_4/Doctrine/en/
第三天的課程: Day 3: The Data Model

在第三天的課程安排中,將可滿足渴望使用文字編輯器編寫PHP程式碼的需求。
接下來的內容有:定義Jobeet的資料模型、使用ORM與資料庫互動、建立應用程式的第一個模組。但這些功能並不需要很多的PHP程式碼。

資料關聯模式(The Relational Model)
在第二天的課程中,有提到Jobeet專案的主要物件有:求才資料(Job)、進階使用(Affiliate)、工作分類(Category),右圖是這些物件的關聯圖。
為了滿足系統在公佈天數等需求,使用了created_at欄位,當資料新增時,會以系統時間紀錄在created_at欄位。同樣的道理,當有資料更新時,會以當時的系統時間紀錄在Updated_at。

資料綱要(The Schema)
很明顯這裡需要使用一個關聯式資料庫來存放求才資料(Job)、進階使用(Affiliate)、工作分類(Category)等資料。
Symfony是一個物件導向的架構,因此盡可能以物件的方式來操作系統。例如:會以物件的方式方來代替寫SQL語法取得資料。
所以關聯式的資料庫必須對應成物件模式,這可以透過ORM(Object-relational Mapping)的工具來達成,Symfony預設提供了Propel和Doctrine兩種ORM的工具,這裡要用的是Doctrine。
ORM需要描述資料表及資料表間的關係,藉此來建立類別的關聯。描述資料綱要(Schema)有兩種方式:檢視已存在的資料庫或以人工的方式來建立。
因Jobeet的資料庫還不存在,在對Jobeet資料庫還不知悉的情況下,就使用以下的內容來建立資料綱要(Schema), config/doctrine/schema.yml

# config/doctrine/schema.yml
JobeetCategory:
  actAs: { Timestampable: ~ }
  columns:
    name: { type: string(255), notnull: true, unique: true }
 
JobeetJob:
  actAs: { Timestampable: ~ }
  columns:
    category_id:  { type: integer, notnull: true }
    type:         { type: string(255) }
    company:      { type: string(255), notnull: true }
    logo:         { type: string(255) }
    url:          { type: string(255) }
    position:     { type: string(255), notnull: true }
    location:     { type: string(255), notnull: true }
    description:  { type: string(4000), notnull: true }
    how_to_apply: { type: string(4000), notnull: true }
    token:        { type: string(255), notnull: true, unique: true }
    is_public:    { type: boolean, notnull: true, default: 1 }
    is_activated: { type: boolean, notnull: true, default: 0 }
    email:        { type: string(255), notnull: true }
    expires_at:   { type: timestamp, notnull: true }
  relations:
    JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: JobeetJobs } 
 
JobeetAffiliate:
  actAs: { Timestampable: ~ }
  columns:
    url:       { type: string(255), notnull: true }
    email:     { type: string(255), notnull: true, unique: true }
    token:     { type: string(255), notnull: true }
    is_active: { type: boolean, notnull: true, default: 0 }
  relations:
    JobeetCategories:
      class: JobeetCategory
      refClass: JobeetCategoryAffiliate
      local: affiliate_id
      foreign: category_id
      foreignAlias: JobeetAffiliates
 
JobeetCategoryAffiliate:
  columns:
    category_id:  { type: integer, primary: true }
    affiliate_id: { type: integer, primary: true }
  relations:
    JobeetCategory:  { onDelete: CASCADE, local: category_id, foreign: id }
    JobeetAffiliate: { onDelete: CASCADE, local: affiliate_id, foreign: id }

在資料庫設定 database.yml 已經OK的情況下(待會才會進行這個步驟),可以使用 symfony doctrine:build-schema來建立資料綱要(schema)。
YAML的參考資料:
  1. http://yaml.org/
  2. http://components.symfony-project.org/yaml/documentation

資料庫的準備
首先準備好一個命名為jobeet的MSQL資料庫。
使用以下指令,讓Jobeet專案,可以使用jobeet資料庫:
symfony configure:database "mysql:host=localhost;dbname=jobeet" root password
在configure:database這個任務中,用了三個參數:PDO DSN、資料庫帳號、密碼
執行configure:database後,資料庫的設定會存放在 config/database.yml,這個設定檔不一定要使用configure:database來產生,也可以自行編輯建立。

使用ORM(Object-relational Mapping)
前面已經建立資料庫的描述 config/doctrine/schema.yml ,接著要使用Doctrine內建的功能來產生建立資料表所需的SQL敘述。
在產生SQL敘述前,要先從綱要檔(Schema)建立模型類別(Model):
c:\dev\sfprojects\jobeet>symfony doctrine:build --model

>> doctrine  generating model classes
>> file+     C:\Users\Hannibal\AppData\Local\Temp/doctrine_schema_52712.yml
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...trine/JobeetAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo.../JobeetAffiliateTable.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...ctrine/JobeetCategory.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...beetCategoryAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...ategoryAffiliateTable.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...e/JobeetCategoryTable.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/model/doctrine/JobeetJob.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...ctrine/JobeetJobTable.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...e/BaseJobeetAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...se/BaseJobeetCategory.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...beetCategoryAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...ne/base/BaseJobeetJob.class.php
>> autoload  Resetting application autoloaders


模型類別(Model Classes)建立後,接著就可以來產生SQL敘述了:
c:\dev\sfprojects\jobeet>symfony doctrine:build --sql

>> doctrine  generating model classes
>> file+     C:\Users\Hannibal\AppData\Local\Temp/doctrine_schema_81084.yml
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...e/BaseJobeetAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...se/BaseJobeetCategory.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...beetCategoryAffiliate.class.php
>> tokens    C:/dev/sfprojects/jobeet/lib/mo...ne/base/BaseJobeetJob.class.php
>> autoload  Resetting application autoloaders
>> file-     C:/dev/sfprojects/jobeet/cache/.../config/config_autoload.yml.php
>> doctrine  generating sql for models
>> dir+      C:\dev\sfprojects\jobeet\data/sql
>> doctrine  Generated SQL successfully for models
以上所產生的SQL敘述語法,存放在 sql\data 目錄下

上述的作業只是產生要建立資料表的SQL敘述語法,建立資料表:

c:\dev\sfprojects\jobeet>symfony doctrine:insert-sql
>> doctrine  creating tables
>> doctrine  created tables successfully



Symfony內建有Help的功能,可以透過 symfony help <task> 取得相關內容
例如:symfony help doctrine:insert-sql

在上述的symfony doctrine:build --model作業裡,所產生的php檔案(位於 lib/model )是用來跟資料庫互動的。
瀏覽這些php檔,可以發現每個資料表有三個類別(class)檔案,以jobeet_job資料表為例:

  1. JobeetJob.class.php:這個類別(class)的一個物件(object),代表jobeet_job資料表的一筆資料。這個類別預設是空的。
    class JobeetJob extends BaseJobeetJob {  }
  2. BaseJobeetJob.class.php:這是JobeetJob的父類別(parent class),每次執行 doctrine:build --model 時,這個類別會被複寫(overwritten),所有的作業會在JobeetJob類別中完成。
  3. JobeetJobTable.class.php:這個類別用來定義方法(Methods),主要用來傳送JobeetJob的物件值。這個類別預設是空的。
    class JobeetJobTable extends Doctrine_Table {
        /**
         * Returns an instance of this class.
         *
         * @return object JobeetJobTable
         */
        public static function getInstance()     {
            return Doctrine_Core::getTable('JobeetJob');
        }
    }

資料欄位的值可以用模型物件(model object)中的存取子(accessor, get*())及更動子(mutator, set*())來操作。例如:
$job = new JobeetJob();
$job->setPosition('Web developer');
$job->save();
echo $job->getPosition();
$job->delete();


也可以直接定義外來鍵(foreign key),把物件連結在一起。例如:
$category = new JobeetCategory();
$category->setName('Programming');
$job = new JobeetJob();
$job->setCategory($category);

在ORM這個段落中的作業項目:doctrine:build --model / doctrine:build --sql / doctrine:insert-sql,以及後續要進行的其他項目:產生表單(form)、資料驗證(validator)...
,可以用doctrine:build --all來代替。例如:
symfony doctrine:build --all --no-confirmation

起始資料(The Initial Data)
經過以上的作業項目,已經在資料庫建立資料表,但資料表中還沒有任何資料。任何的網頁應用程式都會有以下三種資料:

  1. 起始資料(Initial Data):起始資料是應用程式作業所需要的,例如:Jobeet網站需要一些工作分類(category)的起始資料,也需要讓管理者可以登入後台的管理資料。
  2. 測試資料(Test Data):程式開發人員需要一些測試資料來測試網站應用程式。
  3. 使用者資料(User data):使用者在日常作業所產生的資料。
每一次Symfony在資料庫中重新建立資料表,都會清空資料,為使資料庫中的起始資料可以保留下來,可以用php程式碼、在MySQL中執行SQL 敘述語法,這些需求是很普遍常見的,有一個更好的方法,可以用symfony來做。
在 data/fixtures/ 建立 YAML固定檔案,使用doctrine:data-load來將資料載入資料庫中。例如:
# data/fixtures/categories.yml
JobeetCategory:
  design:
    name: Design
  programming:
    name: Programming
  manager:
    name: Manager
  administrator:
    name: Administrator
# data/fixtures/jobs.yml
JobeetJob:
  job_sensio_labs:
    JobeetCategory: programming
    type:         full-time
    company:      Sensio Labs
    logo:         sensio-labs.gif
    url:          http://www.sensiolabs.com/
    position:     Web Developer
    location:     Paris, France
    description:  |
      You've already developed websites with symfony and you want to work
      with Open-Source technologies. You have a minimum of 3 years
      experience in web development with PHP or Java and you wish to
      participate to development of Web 2.0 sites using the best
      frameworks available.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_sensio_labs
    email:        job@example.com
    expires_at:   '2010-10-10'
  job_extreme_sensio:
    JobeetCategory:  design
    type:         part-time
    company:      Extreme Sensio
    logo:         extreme-sensio.gif
    url:          http://www.extreme-sensio.com/
    position:     Web Designer
    location:     Paris, France
    description:  |
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
      eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
      enim ad minim veniam, quis nostrud exercitation ullamco laboris
      nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
      in reprehenderit in.
      Voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      Excepteur sint occaecat cupidatat non proident, sunt in culpa
      qui officia deserunt mollit anim id est laborum.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_extreme_sensio
    email:        job@example.com
    expires_at:   '2010-10-10'

其中jobs.yml這個固定檔案會參考兩張圖片,分別位於以下網址,下載存到 web/uploads/jobs/目錄下
http://www.symfony-project.org/get/jobeet/sensio-labs.gif 
http://www.symfony-project.org/get/jobeet/extreme-sensio.gif

位於 data/fixtures的固定檔案,使用YAML格式編碼,定義了模型物件(object model),以標籤的方式建立唯一的名稱(在jobs.yml中定義了:job_sensio_labs 及 job_extreme_sensio),標籤的最大用處是建立物件的連結關係,不必定義主鍵(primary key),例如:job_sensio_labs的求才工作分類是programming,這個標籤(label)是設定在Programming工作分類。
在YAML檔案中,如果一個字串是由多行文字所組成,必須使用pine字元(|)來串接多行字串,成為一個字串。

雖然一個固定檔案(fixture file)可以包含一到數個模型(Model),這裡採用的方式是一個固定檔案對應一個模型的作法。
Propel requires that the fixtures files be prefixed with numbers to determine the order in which the files will be loaded.
With Doctrine this is not required as all fixtures will be loaded and saved in the correct order to make sure foreign keys are set properly.

固定檔案(fixture file)中,並不需要定義所有的欄位值,symfony可以帶入database schema中的預設值,且當symfony使用doctrine來載入資料到資料庫時,所有內建的行為(built-in behavior)(例如:自動記載產生時生、更新時間)和自訂的行為(custom behavior)都會啟動。
載入起始資料到資料庫的指令 : symfony doctrine:data-load

c:\dev\sfprojects\jobeet>symfony doctrine:data-load
>> doctrine  Loading data fixtures from "C:\...fprojects\jobeet\data/fixtures"
>> doctrine  Data was successfully loaded

可以使用doctrine:build --all --and-load 來代替 doctrine:build --all 和 doctrine:data-load這兩項作業
使用symfony dotrine:build --all --and-load會產生forms, filters, models,並刪除資料庫、重建資料表。

使用瀏覽器查看實際的狀況(See it in Action in the Browser)
接下來是如何顯示工作列表、如何編輯現存的求才資料、如何刪除一筆求才資料,如第一天的進度,Symfony專案是由應用程式組成,每一應用程式可細分為模組(modules),模組中包含表達程式特性的PHP程式碼(例如API模組),或使用者可以在模型物件中進行的各項操作(例如:求財模型Job Model)。
Symfony可以從指定的模型中,自動產生模組,並提供基本的操作
symfony doctrine:generate-module --with-show --non-verbose-templates frontend job JobeetJob

>> dir+      C:\dev\sfprojects\jobeet\apps\frontend\modules/job\actions
>> file+     C:\dev\sfprojects\jobeet\apps\f...s/job\actions/actions.class.php
>> dir+      C:\dev\sfprojects\jobeet\apps\frontend\modules/job\templates
>> file+     C:\dev\sfprojects\jobeet\apps\f...s/job\templates/editSuccess.php
>> file+     C:\dev\sfprojects\jobeet\apps\f.../job\templates/indexSuccess.php
>> file+     C:\dev\sfprojects\jobeet\apps\f...es/job\templates/newSuccess.php
>> file+     C:\dev\sfprojects\jobeet\apps\f...s/job\templates/showSuccess.php
>> file+     C:\dev\sfprojects\jobeet\apps\f...modules/job\templates/_form.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/actions/actions.class.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/templates/editSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f.../job/templates/indexSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...es/job/templates/newSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/templates/showSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...modules/job/templates/_form.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/actions/actions.class.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/templates/editSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f.../job/templates/indexSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...es/job/templates/newSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...s/job/templates/showSuccess.php
>> tokens    C:/dev/sfprojects/jobeet/apps/f...modules/job/templates/_form.php
>> file+     C:\dev\sfprojects\jobeet\test\f...nal\frontend\jobActionsTest.php
>> tokens    C:\dev\sfprojects\jobeet\test\f...nal\frontend\jobActionsTest.php
>> file-     C:/dev/sfprojects/jobeet/cache/...96f/autoJob/templates/_form.php
>> file-     C:/dev/sfprojects/jobeet/cache/...toJob/templates/showSuccess.php
>> file-     C:/dev/sfprojects/jobeet/cache/...utoJob/templates/newSuccess.php
>> file-     C:/dev/sfprojects/jobeet/cache/...oJob/templates/indexSuccess.php
>> file-     C:/dev/sfprojects/jobeet/cache/...toJob/templates/editSuccess.php
>> dir-      C:/dev/sfprojects/jobeet/cache/...db690e723e96f/autoJob/templates
>> file-     C:/dev/sfprojects/jobeet/cache/...toJob/actions/actions.class.php
>> dir-      C:/dev/sfprojects/jobeet/cache/...d7db690e723e96f/autoJob/actions
>> dir-      C:/dev/sfprojects/jobeet/cache/...e90f98f9d7db690e723e96f/autoJob


以上使用了 doctrine:generate-module 來為JobeetJob模型產生 job的前台模組,同時製造了大部分的symfony任務(tasks)、及一些位於 apps\frontend\modules\job\目錄下的檔案及目錄。
actions\ 目錄:The module actions
templates\ 目錄:The module templates

actions\actions.class.php類別:定義了所有job模組的action名稱 / 功能描述,如下:
index : 顯示資料表的所有資料
show : 顯示指定資料的欄位及欄位值
new : 顯示新增資料的表單
create : 產生一筆資料
edit : 顯示編輯資料的表單
update : 根據使用使所送出的值,更新一筆資料
delete : 從資料表刪除一筆指定的資料

可以使用以下的網址,在瀏覽器測試一下job模組:
http://jobeet.localhost/frontend_dev.php/job

沒有留言:

張貼留言