今天工育,無意中從CI論壇的一篇文章里輾轉(zhuǎn)找到了RedBeanPHP——一個簡潔輕巧但功能并不弱的ORM框架:零配置、可以自動創(chuàng)建數(shù)據(jù)庫schema搓彻。核心只有一個300多K的文件如绸,直接在腳本中引用即可使用,還有一些比較獨(dú)特的功能值的開發(fā)人員使用旭贬。
- 當(dāng)前版本4.3.2怔接,需求
- GNU/Linux, BSD, Windows
- PHP 5.3.0 or higher (PHP 5.3.4+ recommended)
- PDO plus driver for your database
- Multibyte String Support
下面是我對其文檔中教程的一個翻譯:
威士忌品嘗記錄教程
在本教程里,我們將通過編寫一個小程序來演示RedBeanPHP的一些基本功能稀轨。讓那些Todo之類的大路貨一邊去扼脐,我們要做的是一個威士忌品嘗記錄(whisky tasting notes)的管理程序。我把它稱之為“一小口(dram)”——意為“一小杯威士忌”奋刽。如果因?yàn)檎軐W(xué)或者宗教原因瓦侮,你反對酒精消費(fèi)的話,你可以把這里的"whisky"換成"tea"佣谐。如同威士忌一樣肚吏,一杯茶帶來的美妙回味同樣深長復(fù)雜,所以這個程序同樣適用于對茶的品鑒狭魂。如果不是反對飲酒而只是單純的不喜歡威士忌的話罚攀,那么就假設(shè)我們是在創(chuàng)建品鑒紅酒党觅、雪茄,或者其他什么——只要你喜歡就行——的程序吧斋泄。
開發(fā)環(huán)境
我們將創(chuàng)建一個CLI(譯:command line interface 命令行接口
)程序杯瞻,在命令行模式下運(yùn)行這就意味著不需要創(chuàng)建圖形界面。這允許我們把注意力放在編寫代碼上是己,而不是糾結(jié)在HTML模板之類的東東上煌茴。我們預(yù)設(shè)的操作系統(tǒng)是UNIX或者GNU/Linux巴粪。(譯:有些命令在windows下略有出入
)
步驟1: 初始化
首先,我們需要下載并安裝RedBeanPHP包刚照。幸運(yùn)的是宙地,這非常簡單摔认。RedBeanPHP只有一個文件,所以我們只需從網(wǎng)上把它抓下來就行了宅粥,就像這樣:
url=http://www.redbeanphp.com/downloadredbean.php
wget $url --output-document="redbeanphp.tar.gz"
tar xvf redbeanphp.tar.gz
windows下直接從這下載好了参袱,戳我下載。下載的壓縮包解壓縮后只有一個文件
rb.php
在本程序中秽梅,我們使用了一個臨時數(shù)據(jù)庫抹蚀,所以在重啟系統(tǒng)之后,數(shù)據(jù)會丟失企垦。
雖然在現(xiàn)實(shí)生活中不是很實(shí)用环壤,這也可算是測試和熟悉RedBeanPHP的一個方法。所以钞诡,讓我們創(chuàng)建程序:dram.php:
touch dram.php
然后用編輯器(我的最愛)打開并編輯:
vim dram.php
譯注:這兩處的命令在windows下可以用:
echo > dram.php
和notepad.exe dram.php
替換郑现。notepad可以替換成你喜歡的編輯器,只要在系統(tǒng)環(huán)境變量PATH中有聲明即可荧降。其實(shí)接箫,windows并不以命令行操作見長:(
雖然我喜歡使用VIM,但具體使用什么編輯器沒啥關(guān)系朵诫,即使是普通的文本編輯器也能工作的很好辛友。下面我們把RedBeanPHP庫包含進(jìn)來并設(shè)置數(shù)據(jù)庫連接:
require 'rb.php';
R::setup();
譯注:這里我在windows下運(yùn)行報錯,提示"Unsupported database()"剪返。新建tmp子目錄废累,然后修改為
R::setup('sqlite:./tmp/red.db')
之后正常。也可以用R::setup('sqlite:memory');
建立臨時內(nèi)存數(shù)據(jù)庫也可随夸。
步驟2: 來一瓶威士忌
在使用RedBeanPHP時九默,重要的一點(diǎn)就是首先創(chuàng)建數(shù)據(jù)記錄。那么宾毒,我們先編寫向數(shù)據(jù)庫中添加數(shù)據(jù)的邏輯驼修。盡管有些人喜歡先創(chuàng)建顯示表中記錄的頁面殿遂,但數(shù)據(jù)庫里起碼得有一些數(shù)據(jù)吧?
RedBeanPHP能替我們完成那些繁重的活——包括創(chuàng)建表乙各、字段等——自動的墨礁,最好還是反過來吧。So耳峦,從你的"add"邏輯開始吧恩静。
$opts = getopt( '', [ 'add:', 'list' ] );
這里我們使用了PHP的getopt()
函數(shù)來從命令行讀取命令。此處蹲坷,我們偵聽兩個命令:add和list∈磺現(xiàn)在,讓我們看看怎么把一瓶威士忌添加到收藏中:
if (isset($opts['add'])) {
$w = R::dispense('whisky');
$w->name = $opts['add'];
$id = R::store($w);
die("OK.\n");
}
這一段代碼非常簡單:它從命令行中獲取add命令的參數(shù)值循签,然后創(chuàng)建一個新的威士忌類型bean级乐。然后把文本賦值給bean的name屬性并保持它。為了讓用戶能看到威士忌的清單县匠,我們也實(shí)現(xiàn)了list功能:
if(isset($opts['list'])){
$bottles = R::find('whisky');
if(!count($bottles)) die("The cellar is empty!\n");
foreach($bottles as $b){
echo "* #{$b->id}: {$b->name} \n";
}
exit;
}
接下來风科,我們就可以使用程序了:
php dram.php --add="Bowmore 12yo"
OK.
php dram.php --add="Lagavulin 16yo"
OK.
這樣了了幾行代碼的程序就可以很好的工作了,但我們可以做的更多乞旦。
geek@beans$ sqlite3 /tmp/red.db
SQLite version 3.7.13 2005-06-11 02:05:32
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
whisky
我們看到whisky表已經(jīng)創(chuàng)建了贼穆,要求的字段也都已經(jīng)就位:
sqlite> .schema
CREATE TALBE `whisky` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT
);
RedBeanPHP自動創(chuàng)建了必須的表和列(字段),數(shù)據(jù)庫的列類型依賴于你要存入的數(shù)據(jù)。RedBeanPHP掃描要存入列的數(shù)據(jù)兰粉,確認(rèn)該列的類型能正確容納你的數(shù)據(jù)故痊。當(dāng)然,你也可以手工調(diào)整數(shù)據(jù)庫結(jié)構(gòu)(schema)亲桦。
步驟3:扔掉幾瓶
接下來崖蜜,我們要添加一個新功能“delete”。是的客峭,這個命令將從數(shù)據(jù)庫中清除指定的記錄豫领。首先我們向getopts添加“delete”命令,目前的命令結(jié)構(gòu)如下:
$opts=getopt('',['add:','list','delete:']);
然后舔琅,寫小一段代碼來執(zhí)行實(shí)際的刪除:
if(iiset($opts['delete'])){
R::trash('whisky',$opts['delete']);
die("Threw the bootle away!\n");
}
好極了等恐,現(xiàn)在我們可以添加、顯示和刪除威士忌了备蚓。試一下:
php dram.php --add="daluaine 16yo"
OK.
php dram.php --list
* #1: Bowmore 12yo
* #3: Daluaine 16yo
靠课蔬!輸錯了。應(yīng)該是Dailuaine而不是Daluaine郊尝。順便說一句:的確很美味二跋。感謝新添加的delete功能,我們可以移除錯誤的記錄并修正這一低級錯誤:
php dram.php --delete=3
Threw the bottle away!
php dram.php --list
* #1: Bowmaore 12yo
php dram.php --add="Dailuaine 16yo"
現(xiàn)在就非常完美了流昏,不過記錄在哪里呢扎即?畢竟跟我們計(jì)劃是做一個管理品嘗記錄的程序巴袒瘛!別急谚鄙,在肉餡羊肚冷掉前各拷,我們就可以添加記錄了。
步驟4:添加品嘗記錄
首先闷营,要考慮一下一條記錄和一瓶威士忌之間的關(guān)系烤黍。一瓶威士忌會有多次被品嘗的記錄,對吧傻盟?那么反過來又是怎么樣呢速蕊?一條品嘗記錄會跟多瓶威士忌有關(guān)嗎?甚至不太可能一瓶酒只有一次品嘗(當(dāng)然啦莫杈,那些非常非常廉價的貨色要除外)互例。
這就意味著我們需要一個一對多的關(guān)系。
一瓶威士忌有多條記錄筝闹,而每一條記錄只屬于一瓶威士忌。這種關(guān)系有時候也被表示為1-N⌒裙猓現(xiàn)在关顷,如果我們對一瓶威士忌不感興趣了將其扔掉,我們還需要保留對應(yīng)的品嘗記錄嗎武福?當(dāng)然不需要啦议双!它們本身沒有任何價值,只是用來描述與之相關(guān)的威士忌的捉片。這就意味著我們必須使用專屬列表(exclusive own list): xownNoteList平痰。我們這樣來關(guān)聯(lián)記錄和威士忌:
$n=R::dispense('note');
$n->note=$text;
$whisky->xownNoteList[]=$n;
R::store($whisky);
注意,list的名稱中包含了我們要保存的bean的類型伍纫。這是一個約定宗雇。list的格式是:
<x> own <BEAN TYPE NAME>List
所以如果我們要在book中保存pages,就得使用ownPageList莹规。因?yàn)槲覀兿MS著瓶子一起扔掉所有記錄赔蒲,所以我們使用了專屬(exclusive)列表。因此良漱,我們用'x'作為list名稱的開頭舞虱。一旦定義了一個專屬列表,就無路可退了母市。如果你確實(shí)要留下記錄矾兜,那你只能使用數(shù)據(jù)庫管理工具(如phpmyadmin)打開數(shù)據(jù)庫,修改外鍵設(shè)置患久。
現(xiàn)在椅寺,添加給用戶顯示指定威士忌的記錄列表的功能吧:
$note=$whisky->xownNoteList;
foreach($note as $note) echo $note->note;
步驟5:包裝一下
現(xiàn)在讓我們看看整個程序浑槽,這是我的版本:
require 'rb.php';
R::setup();
$opts = getopt( '', [
'add:',
'delete:',
'attach-to:',
'note:',
'notes:',
'remove-note:',
'list' ] );
if ( isset( $opts [ 'add' ] ) ) {
$w = R::dispense( 'whisky' );
$w->name = $opts['add'];
$id = R::store( $w );
die( "OK.\n" );
}
if ( isset( $opts['delete'] ) ) {
R::trash( 'whisky', $opts['delete'] );
die( "Threw the bottle away!\n" );
}
if ( isset( $opts['note'] ) && isset( $opts['attach-to'] ) ) {
$w = R::load( 'whisky', $opts['attach-to'] );
if (!$w->id) die( "No such bottle.\n" );
$n = R::dispense( 'note' );
$n->note = $opts['note'];
$w->xownNoteList[] = $n;
R::store( $w );
die( "Added note to whisky.\n" );
}
if ( isset( $opts['notes'] ) ) {
$w = R::load( 'whisky', $opts['notes'] );
foreach( $w->xownNoteList as $note ) {
echo "* #{$note->id}: {$note->note}\n";
}
exit;
}
if ( isset( $opts['remove-note'] ) ) {
R::trash( 'note', $opts['remove-note'] );
die( "Removed note.\n" );
}
if ( isset( $opts['list'] ) ) {
$bottles = R::find( 'whisky' );
if ( !count( $bottles ) ) die( "The cellar is empty!\n" );
foreach( $bottles as $b ) {
echo "* #{$b->id}: {$b->name}\n";
}
exit;
}
下面是在命令行中的具體使用:
php dram.php --add="Dailuaine 16yo"
OK.
php dram.php --list
* #1: Bowmore 12yo
* #4: Dailuaine 16yo
php dram.php --attach-to=4 --note="vanilla, buttered cream"
Added note to whisky.
php dram.php --attach-to=4 --note="apple, pear"
Added note to whisky.
php dram.php --notes=4
* #4: vanilla, buttered cream
* #5: apple, pear
步驟6:玩玩Models
只是出于好玩,我們要添加一個model配并。很多web程序使用MVC架構(gòu)括荡,模型(M)被用來封裝業(yè)務(wù)邏輯。現(xiàn)在溉旋,然我們假設(shè)我們不接受少于四個字符的品嘗記錄畸冲。這就是喝酒業(yè)務(wù)中的業(yè)務(wù)邏輯|規(guī)則:)。為了添加這一規(guī)則的驗(yàn)證观腊,我們需要有一個模型(model)邑闲。在大部分對象關(guān)系映射(object relational mappers--ORM)中,這也是必須首先創(chuàng)建一個完整的類的原因梧油。我很高興的是苫耸,在RedBeanPHP中,事情有一點(diǎn)不同儡陨。我們沒有模型(no model)褪子,記得嗎?只有beans骗村。那么嫌褪,如何從一個bean轉(zhuǎn)到一個model呢?簡單胚股!我們只需要添加一個model笼痛,RedBeanPHP就會自動檢測到其存在±虐瑁基于命名約定缨伊,它將把模型連接到bean。開始吧:
class Model_Note extends RedBean_SimpleModel {
public function update() {
if (strlen($this->bean->note )<4)
die("Note is too short!\n");
}
}
在note模型中进宝,我們可以這樣引用bean:
$this->bean;
一旦我們試圖保存刻坊,bean就會調(diào)用update()方法。雖然沒有辦法停止這一流程即彪,但為了阻止RedBeanPHP保存bean我們必須拋出一個異常紧唱,或者執(zhí)行die()指令操作。讓我們測試一下:
php dram.php --attach-to=4 --note="ap"
Note is too short!
棒極了隶校!工作的很好漏益。看到了嗎深胳?我們不需要更改代碼绰疤,只是簡單的添加一個模型,隨時都行舞终。不需要拿著全部代碼把它們?nèi)揭粋€類里或著東一頭轻庆,西一頭地添加驗(yàn)證規(guī)則癣猾。不需要!只是添加一個模型余爆,然后所有動作就突然全部改從它中間通過了纷宇。除了update()外,我們還可以使用其他各種鉤子(hook)來完成各種模型的工作蛾方。
步驟7:凍結(jié)
在我們發(fā)布程序之前像捶,我們需要確認(rèn)一下數(shù)據(jù)庫并凍結(jié)它(`譯注:以后的操作就不會在改動數(shù)據(jù)庫結(jié)構(gòu)了`)。我們只需要簡單地調(diào)用freeze()即可桩砰,在代碼頂端拓春,位于setup這一行下面:
R::setup();
R::freeze(TRUE);
完工,我們的威士忌程序亚隅!
當(dāng)然啦硼莽,RedBeanPHP還有比CRUD和一對多關(guān)系更多的內(nèi)容,但對一個小教程來說煮纵,想全部覆蓋到那簡直就是一個不可能完成的任務(wù)吖:(
接下來懂鸵,你可以自由的擴(kuò)展這個小程序,添加標(biāo)簽tags行疏,分類categories以及其他概念來熟悉RedBeanPHP提供的各種功能矾瑰。
希望你喜歡!