取势 明道 优术

作者为 扶 凯 发表

这个是 advent 中的 Dancer 中的一个文章,这次翻译的文章,加上我其它翻译的文档,我想对于基本的 Dancer 的使用,就不会有问题了.
原文地址:http://advent.perldancer.org/2010/11


DBIx::Class 也被叫做 DBIC,是为数众多的 Perl ORM (Object Relational Mapper) 中的一个.但是我非常推荐大家记住这个.有人讲 Perl 这个模块是所有语言 ORM 中做得最好的一个.
PS: 为什么要使用ORM,简单来讲,这样可以你不用自己来写 SQL 语句,直接使用面象对象的技术来操作数据库了.

这有一个非常好的介绍建议看看:http://www.slideshare.net/ranguard/dbixclass-beginners-presentation
 

要实现这个,你必须先设置你的 Schema classe. 这个是用来告诉模块你这个数据库的结构和相关的信息的.然后你能使用 DBIC 来实现任何 create, update, delete, search 之类任何 SQL 语句能实现的操作.
在 Dancer 的 web 应用中,我们可以使用 DBIC 的技术是非常容易的,我们得感谢 Dancer::Plugin::DBIC 这个插件.本文是一个简单的 Web 应用的实例,来演示使用的 Dancer::Plugin::DBIC 这个模块.
注意:尽管这个文件讲 DBIX::Class, 但并不会详细的讲这个模块. 如果有必要我们推荐你看看 DBIx::Class::Manual::Intro or DBIx::Class::Manual::Example 来深入了解.

Dancer 书店的例子

让我们来做一个简单的 Dancer 的开发应用(一个用来查找作者和书名的应用).这个应用连接到数据库来查询这些信息.数据库中有作者和书籍二样内容.为了简单这个 web 站点只有一个网页.可以用来查找和显示查询的结果.
为了保持易学,所以这个例子非常短小.这个 HTML 也是非常简单的,只要能完成基本功能就好了.我们就用这个来测试使用 Dancer::Plugin::DBIC.

这是个标准的 MVC 架构,这个应用的结构如下:

  1. Dancer 进行路径的选择和对请求来进行查询的处理,对数据库进行 search 的操作和显示查询的结果,并使用模板技术来显示到网页 view 中.
  2. view 用来显示查询到的任何结果,这个中使用的是 Template Toolkit 来实现.
  3. 设置 model ,关联到数据库上.这包含了 books and authors.这实现 model 是使用的 DBIC 来实现的.本文中使用的是 DBIx::Class.

最基本的,我们需要先创建一个应用,在 Dancer 中非常容易,直接通过 dancer 的命令(cpanm Dancer 安装完会有这个命令).这会生成一些常见的文件夹,都是你的应用所需要的.

$ dancer -a bookstore

下面我简单来讲一下一个 Web 应用所需要的文件夹的作用,这个是上面 dancer -a 的命令都会帮你生成好了的.

三个最常用最重要的文件

  • bookstore/lib/bookstore.pm  # 这个目录是我们最常用的,我们的程序就写在这个 lib 中,默认写在 bookstore.pm 中,dancer 会自动帮你建个 pm 文件和项目名一样的
  • bookstore/config.yml            # 配置文件
  • bookstore/bin/app.pl             # 应用启动时的启动文件,他会自动以模块加载你写的应用.默认 3000 端口

几个常用的目录

  • bookstore/views                 # 标准 MVC 中的 view 模板就放这个目录
  • bookstore/public/css          # public 是放静态文件的 css 中放 css
  • bookstore/public/javascripts   # 这个是放 js 脚本的

 

修改template 的模板技术

我们在这要显示作者 authors 和书籍 books 二个信息,所以修改使用 Template Toolkit 来实现对 MYSQL 查询出来数据结构的进行解析,比起 dancer 默认设置的 Dancer::Template::Simple 模块更加好更加容易.
所以我们修改配置文件中来使用 Template Toolkit 的模板引擎,我们常常叫 Template Toolkit 为 TT ,下面使用 TT 来简写这个.

# 增加 bookstore/config.yml
template: template_toolkit

创建显示网页(view)

我们需要一个网页来显示搜索到的下面这样的结果.这个结果会通过所谓的路径选择(route) 给 TT 的模板生成 view 来解析这个数组引用.数组引用内部是一个 hash 的引用.内部作者 author 的 key 包含着作者的名字,books 的 key 包含着另一个 arrayref 放着书名的引用字符.
结果的数据结构的例子:

# example of a list of results
[ { author => 'author 1',
    books => [ 'book 1', 'book 2' ],
  },
  { author => 'author 2',
    books => [ 'book 3', 'book 4' ],
  }
]

所以,怎么样在 view 上显示这些内容啦,下面的例子显示这个搜索框和搜索结果.这是使用的 TT, 只是 Dancer 给默认的 [% %] 使用了 <% %>  来替换,其它的还是一样.
在 bookstore/views/search.tt 的视图文件的内容如下

# bookstore/views/search.tt
<p>
<form action="/search">
Search query: <input type="text" name="query" />
</form>
</p>
<br>  
  
<% IF query.length %>
  <p>Search query was : <% query %>.</p>
  <% IF results.size %>
    Results:
    <ul>
    <% FOREACH result IN results %>
      <li>Author: <% result.author.replace("((?i)$query)", '<b>$1</b>') %>
      <ul>
      <% FOREACH book IN result.books %>
        <li><% book.replace("((?i)$query)", '<b>$1</b>') %>
      <% END %>
      </ul>
    <% END %>
  <% ELSE %>
    No result
  <% END %>
<% END %>

 

建一个路径选择的函数来实现功能route

这个 Dancer 的 route 没有什么花的东西,只要增加到 bookstore/lib/bookstore.pm 的模块中就行:

# add in bookstore/lib/bookstore.pm
get '/search' => sub {
    my $query = params->{query};
    my @results = ();
    if (length $query) {
        @results = _perform_search($query);
    }
    template 'search', { query => $query,
                         results => \@results,
                       };
};

这是比较简单的,取得那个传进来的 query 字段的参数,如果这个参数有值的话,就进行 search 的操作,然后在通过 view 来显示.
正如你见到的,我们必须写一个叫 _perform_search() 的方法,我们后在谈这个,我们先建一个有数据的数据库

建一个数据库
我们这次使用的是 SQLite ,对于这个简单的例子他非常合适,这个要替换成 MYSQL 只要修改一行就能实现,下面我们来使用 SQLite 建一个数据库.

$ sqlite3 bookstore.db
CREATE TABLE author( 
  id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
  firstname text default '' not null, 
  lastname text not null);

CREATE TABLE book(
  id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
  author INTEGER REFERENCES author (id), 
  title text default '' not null );

这建了二个表,一个 author,一个 book .
我们来使用脚本生成一些测试的数据到数据库中,我们来使用 DBIX::Class来加入数据.

# populate_database.pl
package My::Bookstore::Schema;
use base qw(DBIx::Class::Schema::Loader);
package main;
my $schema = My::Bookstore::Schema->connect('dbi:SQLite:dbname=bookstore.db');
$schema->populate('Author', [
  [ 'firstname', 'lastname'],
  [ 'Ian M.',    'Banks'   ],
  [ 'Richard',   'Matheson'],
  [ 'Frank',     'Herbert' ],
]);
my @books_list = (
  [ 'Consider Phlebas',    'Banks'    ],
  [ 'The Player of Games', 'Banks'    ],
  [ 'Use of Weapons',      'Banks'    ],
  [ 'Dune',                'Herbert'  ],
  [ 'Dune Messiah',        'Herbert'  ],
  [ 'Children of Dune',    'Herbert'  ],
  [ 'The Night Stalker',   'Matheson' ],
  [ 'The Night Strangler', 'Matheson' ],
);
# transform author names into ids
$_->[1] = $schema->resultset('Author')->find({ lastname => $_->[1] })->id
  foreach (@books_list);
$schema->populate('Book', [
  [ 'title', 'author' ],
  @books_list,
]);

这个脚本,在放着上面建好的 bookstore.db 的目录中.然后运行这个脚本加入一些我们自己生成的测试数据

$ perl populate_database.pl

 

使用 Dancer::Plugin::DBIC
现在我们回到 Dancer 的应用中.这次不是使用SQL与数据库交互,这次使用的需要配置是 DBIX::Class. DBIC 需要知道你的数据怎么从数据库中取出来.有这二方法来让 DBIC 知道:
写一个自己 Perl 的模块,来实现和加载 schema .这个 schema 需要连接到数据库.
另外,让现有的 DBIC 来连接数据库,然后整个应用中都全局的使用这个来操作.
我们将展示使用这二种解决方案.实现这个功能有大量的方法可以做到,但是非常复杂,所以我们才需要 DBIC .
我们来加添加一些配置文件到 Dancer 的应用中,需要指出我们要使用的插件是 Dancer::Plugin::DBIC .我们定义新的 DBIC 的 schema 名字就叫 bookstore .并指定连接 SQLite 的数据库的方法

# add in bookstore/config.yml
plugins:
  DBIC:
    bookstore:
      dsn:  "dbi:SQLite:dbname=bookstore.db"

我们还可以定义多个 schemas ,但需要加入更多的 DBIC 的字段的配置.
这时在应用加载的时候,会根据这个中的配置的内容,自动的通过这个 schema 的配置连接到数据库.但要确保 Dancer::Plugin::DBIC 这个模块安装了,并是可用的,数据库也是可连接的.不要配置错了.
然后在我们的 Dancer 应用中加载 Dancer::Plugin::DBIC 这个模块就行了,这是非常容易的:

# add in bookstore/lib/bookstore.pm
use Dancer::Plugin::DBIC;

记的我们前面的一个子函数没 _perform_search 没,他会使用 Dancer::Plugin::DBIC 的插件来访问 schema 中指出来的数据库.我们只需要使用这个中的内部函数 'schema' 来返回这个 schema 中的数据库连接就行. schema 'bookstore' 这个会通过 DBIx::Class::Schema::Loader 来返回这个对象.他就是标准的 DBIx::Class 了.其它使用这个返回的引用时就直接是 DBIx::Class 的语法了.

# add in bookstore/lib/bookstore.pm
sub _perform_search {
    my ($query) = @_;
    my $bookstore_schema = schema 'bookstore';
    my @results;
    # search in authors
    my @authors = $bookstore_schema->resultset('Author')->search({
      -or => [
        firstname => { like => "%$query%" },
        lastname  => { like => "%$query%" },
      ]
    });
    push @results, map {
        { author => join(' ', $_->firstname, $_->lastname),
          books => [],
        }
    } @authors;
    my %book_results;
    # search in books
    my @books = $bookstore_schema->resultset('Book')->search({
        title => { like => "%$query%" },
    });
    foreach my $book (@books) {
        my $author_name = join(' ', $book->author->firstname, $book->author->lastname);
        push @{$book_results{$author_name}}, $book->title;
    }
    push @results, map {
        { author => $_,
          books => $book_results{$_},
        }
    } keys %book_results;
    return @results;
}

上面我们使用 results 来存放我们所查找到的书的结果.

 

使用自己写的 schema 的类
我们怎么写自己的DBIC 的类超出了本文的范围了. 你可以使用 DBIx::Class::Schema::Loader 来生成.你也可以自己完全从头开始写起.第三种选择是使用 DBIx::Class::MooseColumns 他可以让你的是 DBIC schema 类框架中使用了 Moose.
你需要给你的 schema 的类放到 Dancer 的 bookstor/lib/ 中.
如果放好后,你可以修改你的 config.yml 中指定使用你自己的 schema 来替换来默认的自动发现的方法

# change in bookstore/config.yml
plugins:
  DBIC:
    bookstore:
      schema_class: My::Bookstore::Schema
      dsn: "dbi:SQLite:dbname=bookstore.db"

 

 

启动我们的应用
开始启动我们的这个应用

$ perl bookstore/bin/app.pl

现在你可以使用这个来搜索了.

 

你可以看到,这页有 4 个结果.首先是找到的作者,接下来是他的书名.
这真是一个很长的文章,我想展示给你怎么样在 Dancer 中来使用 DBIC.基本上所有的 Dancer 的插件都有一个共同点“应尽可能简单“,这正是我们希望我们展示给你的.
AUTHOR
dams ( Damien Krotkine <dams@cpan.org> )

FQA : 怎么样加入 MySQL 的支持
给上面 SQLite 的 dns 修改成下面这样就行

plugins:
  DBIC:
    bookstore:
#      dsn: "dbi:SQLite:dbname=bookstore.db"
      dsn: "DBI:mysql:test:192.168.0.xx:3306"
      user: "root"
      pass: "pass"


 

 

来了就留个评论吧! 7个评论



    丰禾棋牌 2011年06月23日 的 01:54

    建立数据库呀。我对这个完全不懂呢。

    石阳 2011年06月23日 的 02:09

    斯文牛氓 2011年07月15日 的 04:00

    凯哥大作 顶一下

    xiaochong 2012年03月19日 的 09:09

    hi 扶凯 你好 你有没有出现过,dancer中改成mysql的库后数据乱码的问题?

      扶 凯 2012年03月20日 的 02:25

      直接使用 utf8 连接的时候

    netwrom 2012年12月27日 的 06:55

    插件安装正常,无法使用 dbicdump
    [root@DB235 /]# cpanm Dancer::Plugin::DBIC
    Dancer::Plugin::DBIC is up to date. (0.1601)