美文网首页PHP实战
RedBeanPHP——介绍一个ORM框架

RedBeanPHP——介绍一个ORM框架

作者: 易木成华 | 来源:发表于2016-09-18 16:10 被阅读814次

    今天,无意中从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提供的各种功能。
      希望你喜欢!

    相关文章

      网友评论

        本文标题:RedBeanPHP——介绍一个ORM框架

        本文链接:https://www.haomeiwen.com/subject/yczwettx.html