Teng で CRUD をしてみる

id:nekokak さんが開発されている、Teng で CRUD を一通りやってみました。

僕自身、DBIx::Class を以前使っていたんですが、あまりにも機能が富豪的すぎて使うのをやめてしまいました。それからは生 DBI を使っていたんですが、開発効率を考えたときに、ORM は使うべきだと再度考えるようになり、軽量な ORM、Teng に注目している所です。

準備

テスト用のテーブル

+------------+-----------+------+-----+---------------------+-----------------------------+
| Field      | Type      | Null | Key | Default             | Extra                       |
+------------+-----------+------+-----+---------------------+-----------------------------+
| id         | int(11)   | NO   | PRI | NULL                | auto_increment              |
| title      | text      | NO   |     | NULL                |                             |
| body       | text      | NO   |     | NULL                |                             |
| created_at | datetime  | NO   |     | 0000-00-00 00:00:00 |                             |
| updated_at | timestamp | YES  |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
+------------+-----------+------+-----+---------------------+-----------------------------+

まずは、モデルとスキーマのクラスを作成

package My::DB;
use strict;
use warnings;
use parent 'Teng';

package My::DB::Schema;
use strict;
use warnings;
use Teng::Schema::Declare;
use DateTime::Format::MySQL;

table {
    name 'memo';
    pk 'id';
    columns qw( id title body created_at updated_at );

    inflate qr/_at$/ => sub {
        DateTime::Format::MySQL->parse_datetime(shift);
    };
    deflate qr/_at$/ => sub {
        DateTime::Format::MySQL->format_datetime(shift);
    };
};

1;

inflate/deflate の第一引数には正規表現が書けるので、複数のカラムを一度に inflate/deflate 設定できるようです。

データの作成

use strict;
use warnings;
use feature qw( say );
use lib "lib";

use My::DB;
use DateTime;
use Data::Dumper;

my $teng = My::DB->new(connect_info => [
    'dbi:mysql:database=testdb',
    'test',
    'tes10',
    +{ RaiseError => 1, PrintError => 0, AutoCommit => 1, },
]);

my $row = $teng->insert(memo => +{
    id => 1, # auto_incrementを指定しているけどテスト用に固定値を入れる
    title => 'Hello',
    body  => 'World',
    created_at => DateTime->now(time_zone => 'local'),
});

say Dumper($row->get_columns);

# Teng::Row が必要ない場合
my $last_insert_id = $teng->fast_insert(memo => +{
    id => 2,
    title => 'red',
    body  => 'green',
    created_at => DateTime->now(time_zone => 'local'),
});

say "last_insert_id: ", $last_insert_id;

# テスト用データを作成
$teng->fast_insert(memo => +{
    id => 3,
    title => 'black',
    body  => 'white',
    created_at => DateTime->now(time_zone => 'local'),
});

$teng->fast_insert(memo => +{
    id => 4,
    title => 'foo',
    body  => 'bar baz',
    created_at => DateTime->now(time_zone => 'local'),
});

Teng#insert を実行すると、Teng::Row オブジェクトが返ってきます。必要ない場合は、Teng#fast_insert が使えます。

実行結果

$VAR1 = {
          'body' => 'World',
          'created_at' => '2011-11-21 00:30:47',
          'updated_at' => '2011-11-21 00:30:47',
          'id' => '1',
          'title' => 'Hello'
        };

last_insert_id: 2

データの検索

use strict;
use warnings;
use feature qw( say );
use lib "lib";

use My::DB;
use DateTime;
use Data::Dumper;

my $teng = My::DB->new(connect_info => [
    'dbi:mysql:database=testdb',
    'test',
    'tes10',
    +{ RaiseError => 1, PrintError => 0, AutoCommit => 1, },
]);

my $iter = $teng->search(memo => +{ id => [1, 2] });
while (my $row = $iter->next) {
    say Dumper($row->get_columns);
    say $row->created_at->ymd; # DateTime
}
say "";

# 一件のみ取得
my $memo = $teng->single(memo => +{ id => 2 });
say Dumper($memo->get_columns);
say $memo->created_at->ymd; # DateTime

複数行を取得する時は、Teng#search が使えます。Teng::Iterator オブジェクトが返り値となり、Teng::Iterator#next で Teng::Row オブジェクトが取得できます。

一行だけ取得する場合は、Teng#single が使えます。created_at カラムは、DateTime オブジェクトになっています。なお、Teng::Row#get_columns で取得した場合は、単純な値になるみたいです。

実行結果

$VAR1 = {
          'body' => 'World',
          'created_at' => '2011-11-21 00:30:47',
          'updated_at' => '2011-11-21 00:30:47',
          'id' => '1',
          'title' => 'Hello'
        };

2011-11-21
$VAR1 = {
          'body' => 'green',
          'created_at' => '2011-11-21 00:30:47',
          'updated_at' => '2011-11-21 00:30:47',
          'id' => '2',
          'title' => 'red'
        };

2011-11-21

$VAR1 = {
          'body' => 'green',
          'created_at' => '2011-11-21 00:30:47',
          'updated_at' => '2011-11-21 00:30:47',
          'id' => '2',
          'title' => 'red'
        };

2011-11-21

データの更新

use strict;
use warnings;
use feature qw( say );
use lib "lib";

use My::DB;
use DateTime;
use Data::Dumper;

my $teng = My::DB->new(connect_info => [
    'dbi:mysql:database=testdb',
    'test',
    'tes10',
    +{ RaiseError => 1, PrintError => 0, AutoCommit => 1, },
]);

my $iter = $teng->search(memo => +{ id => [1, 2] });
while (my $row = $iter->next) {
    $row->update({ body => 'update test' });
}

say $teng->single(memo => +{ id => 1 })->body;
say $teng->single(memo => +{ id => 2 })->body;

Teng#update を実行します。難しいところはないと思います。

実行結果

update test
update test

データの削除

use strict;
use warnings;
use feature qw( say );
use lib "lib";

use My::DB;
use DateTime;
use Data::Dumper;

my $teng = My::DB->new(connect_info => [
    'dbi:mysql:database=testdb',
    'test',
    'tes10',
    +{ RaiseError => 1, PrintError => 0, AutoCommit => 1, },
]);

my $rows = $teng->delete(memo => +{ id => 1 } );
say "deleted rows: ", $rows; # 削除した件数

# 別なやり方
my $iter = $teng->search(memo => +{ id => [2, 4] });
while (my $row = $iter->next) {
    $row->delete;
}

Teng#delete と Teng::Row#delete を使用することができます。

実行結果

deleted rows: 1

使ってみた印象

実際に使ってみたところ、非常に簡単に使えるなと言うのが第一印象です。また、Web開発をするにあたり、通常使用する分には必要十分な機能を兼ね揃えていると思います。

最後に

今回は入門編として、トランザクション等の考慮はしておらず、とにかく Teng を触って覚えることを主旨としました。今後は、Teng でのリレーションの仕方等を覚えていこうと思います。