RedBeanPHP——介绍一个ORM框架

今天,无意中从CI论坛的一篇文章里辗转找到了RedBeanPHP——一个简洁轻巧但功能并不弱的ORM框架:零配置、可以自动创建数据库schema。核心只有一个300多K的文件,直接在脚本中引用即可使用,还有一些比较独特的功能值的开发人员使用。

  • 当前版本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)”——意为“一小杯威士忌”。如果因为哲学或者宗教原因,你反对酒精消费的话,你可以把这里的”whisky”换成”tea”。如同威士忌一样,一杯茶带来的美妙回味同样深长复杂,所以这个程序同样适用于对茶的品鉴。如果不是反对饮酒而只是单纯的不喜欢威士忌的话,那么就假设我们是在创建品鉴红酒、雪茄,或者其他什么——只要你喜欢就行——的程序吧。

开发环境

我们将创建一个CLI(译:command line interface 命令行接口)程序,在命令行模式下运行这就意味着不需要创建图形界面。这允许我们把注意力放在编写代码上,而不是纠结在HTML模板之类的东东上。我们预设的操作系统是UNIX或者GNU/Linux。(译:有些命令在windows下略有出入

步骤1: 初始化

首先,我们需要下载并安装RedBeanPHP包。幸运的是,这非常简单。RedBeanPHP只有一个文件,所以我们只需从网上把它下来就行了,就像这样:

url=http://www.redbeanphp.com/downloadredbean.php
wget $url --output-document="redbeanphp.tar.gz"
tar xvf redbeanphp.tar.gz

windows下直接从这下载好了,戳我下载。下载的压缩包解压缩后只有一个文件

rb.php

在本程序中,我们使用了一个临时数据库,所以在重启系统之后,数据会丢失。
  虽然在现实生活中不是很实用,这也可算是测试和熟悉RedBeanPHP的一个方法。所以,让我们创建程序:dram.php:

touch dram.php

然后用编辑器(我的最爱)打开并编辑:

vim dram.php

译注:这两处的命令在windows下可以用:echo > dram.phpnotepad.exe dram.php替换。notepad可以替换成你喜欢的编辑器,只要在系统环境变量PATH中有声明即可。其实,windows并不以命令行操作见长:(

虽然我喜欢使用VIM,但具体使用什么编辑器没啥关系,即使是普通的文本编辑器也能工作的很好。下面我们把RedBeanPHP库包含进来并设置数据库连接:

require 'rb.php';
R::setup();

译注:这里我在windows下运行报错,提示”Unsupported database()”。新建tmp子目录,然后修改为R::setup('sqlite:./tmp/red.db')之后正常。也可以用R::setup('sqlite:memory');建立临时内存数据库也可。

步骤2: 来一瓶威士忌

在使用RedBeanPHP时,重要的一点就是首先创建数据记录。那么,我们先编写向数据库中添加数据的逻辑。尽管有些人喜欢先创建显示表中记录的页面,但数据库里起码得有一些数据吧?
  RedBeanPHP能替我们完成那些繁重的活——包括创建表、字段等——自动的,最好还是反过来吧。So,从你的”add”逻辑开始吧。

$opts = getopt( '', [ 'add:', 'list' ] );

这里我们使用了PHP的getopt()函数来从命令行读取命令。此处,我们侦听两个命令:addlist。现在,让我们看看怎么把一瓶威士忌添加到收藏中:

if (isset($opts['add'])) {
    $w = R::dispense('whisky');
    $w->name = $opts['add'];
    $id = R::store($w);
    die("OK.\n");
}

这一段代码非常简单:它从命令行中获取add命令的参数值,然后创建一个新的威士忌类型bean。然后把文本赋值给bean的name属性并保持它。为了让用户能看到威士忌的清单,我们也实现了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表已经创建了,要求的字段也都已经就位:

sqlite> .schema
CREATE TALBE `whisky` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT
);

RedBeanPHP自动创建了必须的(字段),数据库的列类型依赖于你要存入的数据。RedBeanPHP扫描要存入的数据,确认该列的类型能正确容纳你的数据。当然,你也可以手工调整数据库结构(schema)。

步骤3:扔掉几瓶

接下来,我们要添加一个新功能“delete”。是的,这个命令将从数据库中清除指定的记录。首先我们向getopts添加“delete”命令,目前的命令结构如下:

$opts=getopt('',['add:','list','delete:']);

然后,写小一段代码来执行实际的删除:

if(iiset($opts['delete'])){
    R::trash('whisky',$opts['delete']);
    die("Threw the bootle away!\n");
}

好极了,现在我们可以添加、显示和删除威士忌了。试一下:

php dram.php --add="daluaine 16yo"
 OK.
 php dram.php --list
* #1: Bowmore 12yo
* #3: Daluaine 16yo

靠!输错了。应该是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"

现在就非常完美了,不过记录在哪里呢?毕竟跟我们计划是做一个管理品尝记录的程序啊!别急,在肉馅羊肚冷掉前,我们就可以添加记录了。

步骤4:添加品尝记录

首先,要考虑一下一条记录一瓶威士忌之间的关系。一瓶威士忌会有次被品尝的记录,对吧?那么反过来又是怎么样呢?条品尝记录会跟瓶威士忌有关吗?甚至不太可能一瓶酒只有一次品尝(当然啦,那些非常非常廉价的货色要除外)。
  这就意味着我们需要一个一对多的关系。
  瓶威士忌有条记录,而每一条记录只属于瓶威士忌。这种关系有时候也被表示为1-N。现在,如果我们对一瓶威士忌不感兴趣了将其扔掉,我们还需要保留对应的品尝记录吗?当然不需要啦!它们本身没有任何价值,只是用来描述与之相关的威士忌的。这就意味着我们必须使用专属列表(exclusive own list): xownNoteList。我们这样来关联记录和威士忌:

$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。因为我们希望随着瓶子一起扔掉所有记录,所以我们使用了专属(exclusive)列表。因此,我们用‘x’作为list名称的开头。一旦定义了一个专属列表,就无路可退了。如果你确实要留下记录,那你只能使用数据库管理工具(如phpmyadmin)打开数据库,修改外键设置。
  现在,添加给用户显示指定威士忌的记录列表的功能吧:

$note=$whisky->xownNoteList;
foreach($note as $note) echo $note->note;

步骤5:包装一下

现在让我们看看整个程序,这是我的版本:

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架构,模型(M)被用来封装业务逻辑。现在,然我们假设我们不接受少于四个字符的品尝记录。这就是喝酒业务中的业务逻辑|规则:)。为了添加这一规则的验证,我们需要有一个模型(model)。在大部分对象关系映射(object relational mappers–ORM)中,这也是必须首先创建一个完整的类的原因。我很高兴的是,在RedBeanPHP中,事情有一点不同。我们没有模型(no model),记得吗?只有beans。那么,如何从一个bean转到一个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就会调用update()方法。虽然没有办法停止这一流程,但为了阻止RedBeanPHP保存bean我们必须抛出一个异常,或者执行die()指令操作。让我们测试一下:

php dram.php --attach-to=4 --note="ap"
Note is too short! 

棒极了!工作的很好。看到了吗?我们不需要更改代码,只是简单的添加一个模型,随时都行。不需要拿着全部代码把它们塞到一个类里或着东一头,西一头地添加验证规则。不需要!只是添加一个模型,然后所有动作就突然全部改从它中间通过了。除了update()外,我们还可以使用其他各种钩子(hook)来完成各种模型的工作。

步骤7:冻结

在我们发布程序之前,我们需要确认一下数据库并冻结它(`译注:以后的操作就不会在改动数据库结构了`)。我们只需要简单地调用freeze()即可,在代码顶端,位于setup这一行下面:

R::setup();
R::freeze(TRUE);

完工,我们的威士忌程序!
  当然啦,RedBeanPHP还有比CRUD一对多关系更多的内容,但对一个小教程来说,想全部覆盖到那简直就是一个不可能完成的任务吖:(
  接下来,你可以自由的扩展这个小程序,添加标签tags,分类categories以及其他概念来熟悉RedBeanPHP提供的各种功能。
  希望你喜欢!

    原文作者:易木成华
    原文地址: https://www.jianshu.com/p/243168fa358d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞