Showing 4 changed files with 549 additions and 16 deletions
+2 -2
lib/DBIx/Custom/Guides.pod
... ...
@@ -391,12 +391,12 @@ SQLite
391 391
     
392 392
     # DATETIME to Time::Piece object
393 393
     datetime_to_tp => sub {
394
-        return Time::Piece->strptime(shift, $FORMATS->{db_datetime});
394
+        return Time::Piece->strptime(shift, '%Y-%m-%d %H:%M:%S');
395 395
     }
396 396
     
397 397
     # DATE to Time::Piece object
398 398
     date_to_tp => sub {
399
-        return Time::Piece->strptime(shift, $FORMATS->{db_date});
399
+        return Time::Piece->strptime(shift, '%Y-%m-%d');
400 400
     }
401 401
 
402 402
 =head2 6.Create table object
+542 -14
lib/DBIx/Custom/Guides/Ja.pod
... ...
@@ -8,7 +8,8 @@ DBIx::Custom::Guides::Ja - DBIx::Customの日本語ガイド
8 8
 
9 9
 L<DBIx::Custom>はデータベースへのクエリの発行を簡単に行うための
10 10
 クラスです。L<DBIx::Class>やL<DBIx::Simple>と同じように
11
-L<DBI>のラッパクラスになっています。
11
+L<DBI>のラッパクラスになっています。L<DBIx::Class>よりも簡単に、
12
+L<DBIx::Simple>よりもはるかに柔軟なことを行うことができます。
12 13
 
13 14
 L<DBIx::Custom>はO/Rマッパーではありません。O/Rマッパーは
14 15
 便利ですが、O/Rマッパのたくさんの文法を覚える必要があります。
... ...
@@ -17,10 +18,10 @@ L<DBIx::Custom>はO/Rマッパーではありません。O/Rマッパーは
17 18
 生のSQLを発行しなければならない場合がたくさんあります。
18 19
 
19 20
 L<DBIx::Custom>はO/Rマッパとは対照的な設計が行われています。
20
-L<DBIx::Custom>の主な目的は、SQLを尊重しつつ、DBIだけでは
21
+L<DBIx::Custom>の主な目的は、SQLを尊重しつつ、L<DBI>だけでは
21 22
 とてもめんどうな作業を簡単にすることです。もしSQLについて
22 23
 多くの知識を持っているならば、L<DBIx::Custom>でそのまま
23
-生かすことができます。
24
+活用することができます。
24 25
 
25 26
 L<DBIx::Custom>の仕組みを簡単に説明しておきましょう。
26 27
 L<DBIx::Custom>では、タグと呼ばれるものを
... ...
@@ -31,25 +32,132 @@ SQLの中に埋め込むことができます。
31 32
 {}で囲まれた部分がタグです。このSQLは実際に実行されるときには
32 33
 次のようにプレースホルダに展開されます。
33 34
 
34
-    select * from book where title = ? and title = ?;
35
+    select * from book where title = ? and author = ?;
35 36
 
36 37
 これらの展開にはどのような意味があるのでしょうかと質問
37
-されることかと思います。
38
+されることかと思います。この簡単な仕組みの上に非常にたくさんの
39
+有用で便利で使いやすい機能が構築されます。それは以下のようなものです。
38 40
 
41
+=over 4
39 42
 
43
+=item 1. プレースホルダのパラメータをハッシュリファレンスで指定
40 44
 
45
+L<DBI>をそのまま使うのであればプレースホルダのパラメータは配列
46
+で指定する必要があります。
41 47
 
42
-またSQLを簡単に実行するための、
48
+    $sth->execute(@bind);
49
+
50
+L<DBIx::Custom>を利用するのであればハッシュリファレンスで指定すること
51
+できます。
52
+    
53
+    my $param = {title => 'Perl', author => 'Ken'};
54
+    $dbi->execute($sql, $param);
55
+
56
+=item 2. パラメータのフィルタリング
57
+
58
+たとえば、日付の列は、Perlで扱うときにはC<Time::Piece>などの日付オブジェクト
59
+で扱い、データベースに格納するときはデータベースの日付型に変換したい
60
+と思うのではないでしょうか。またデータベースから取り出すときは
61
+データベースの日付型から日付オブジェクトに変換したと思うのでは
62
+ないでしょうか。
63
+
64
+このようなときはフィルタ機能を使うことができます。
65
+
66
+まずフィルタを登録します。
67
+
68
+    $dbi->register_filter(
69
+        tp_to_date => sub {
70
+            ...
71
+        },
72
+        date_to_tp => sub {
73
+            ...
74
+        }
75
+    );
76
+
77
+次にテーブルの各列にこのフィルタを適用します。
78
+
79
+    $dbi->apply_filter('book',
80
+        'publish_date' => {out => 'tp_to_date', in => 'date_to_tp'}
81
+    );
82
+
83
+outはPerlからデータベースに保存する方向、inはデータベースからPerlに取得する方向です。
84
+
85
+SQLを発行するときにテーブルの指定を行えば、自動的にこのフィルタが適用されます。
86
+
87
+    $dbi->execute($sql, $param, table => 'book');
88
+
89
+=item 3. 選択的な検索条件
90
+
91
+生のDBIを利用しているとき一番たいへんなのは選択的な検索条件を作成したいときです。
92
+
93
+たとえば、検索条件にtitleとauthorが指定された場合は次のSQLを
94
+
95
+    select * from book where title = ? and author = ?;
96
+
97
+titleだけの場合は次のSQLを
98
+
99
+    select * from book where title = ?;
100
+    
101
+authorだけの場合は次のSQLを発行した場合を考えましょう。
102
+
103
+    select * from book where author = ?;
104
+
105
+これはとても大変な作業なので、通常はL<SQL::Abstract>を動的に生成してくれる
106
+モジュールを利用することになります。
107
+
108
+L<DBIx::Custom>はさらに簡単で便利な方法を用意しています。
109
+
110
+    my $where = $dbi->where;
111
+    $where->param({title => 'Perl'});
112
+    $where->clause(
113
+        ['and', '{= title}', {'= author'}]
114
+    );
115
+
116
+    my $sql = "select * from book $where";
117
+
118
+詳しい説明は後ほど行いますが、上記のように記述すれば、
119
+L<DBIx::Custom>では選択的な検索条件を持つWhere句を生成することができます。
120
+検索条件が入れ子になった構造やorについても対応しています。
121
+
122
+=item 4. 挿入、更新、削除、選択を行うためのメソッド
123
+
124
+L<DBIx::Custom>ではSQLをさらに簡単に実行するための
125
+メソッドも提供しています。
43 126
 C<insert()>, C<update()>, C<delete()>,C<select()>などの
44
-シュガーメソッドも提供します。
127
+シュガーメソッドを使って、挿入、更新、削除、選択という操作を行うことが
128
+できます。
129
+
130
+    my $param = {title => 'Perl', author => 'Ken'};
131
+    $dbi->insert(table => 'book', param => $param);
132
+
133
+=item 5. テーブル単位の操作の登録
134
+
135
+テーブルに対して操作を登録することができます。これによって
136
+テーブル名を繰り返し指定する必要がなくなり、ソースコードの
137
+見通しが良くなります。
138
+
139
+    $dbi->talbe('book',
140
+        list => sub {
141
+            ...
142
+        },
143
+        list_somethin => sub {
144
+            
145
+        }
146
+    );
147
+
148
+登録したメソッドはそのまま利用することができます。
149
+
150
+    $dbi->table('book')->list;
45 151
 
46
-L<DBIx::Custom>はSQLを尊重します。SQLはとても複雑で、美しくはありません。
47
-けれども、SQLはデファクトスタンダードな技術です。
48
-ですので、データベースを学ぶすべての人はSQLを知っています。
49
-あなたがすでにSQLを知っているなら、L<DBIx::Custom>を使って
50
-何かを行うために覚えることはとても少ないです。
152
+通常O/Rマッパはテーブルに対応するクラスを作成しなければ
153
+ならないことが多いですが、L<DBIx::Custom>ではこの作業を簡便に
154
+しており、上記のように登録することができます。
51 155
 
52
-では使い方を解説します。
156
+=back
157
+
158
+L<DBIx::Custom>はL<DBI>を補うとても便利なモジュールです。
159
+興味をもたれた方は、この後で詳しい解説を行いますので、
160
+ご覧になってみてください。
53 161
 
54 162
 =head2 1. データベースへの接続
55 163
 
... ...
@@ -238,4 +346,424 @@ C<delete()>、C<select>メソッドで使用することが
238 346
     $dbi->insert(table  => 'book',
239 347
                  param  => {title => 'Perl', author => 'Ken'});
240 348
                  filter => {title  => 'encode_utf8',
241
-                            a
349
+                            author => 'encode_utf8'});
350
+
351
+C<filter>オプションは、C<insert()>、C<update()>、C<update_all()>
352
+C<delete()>、C<select>メソッドで使用することが
353
+できます。
354
+
355
+C<select()>メソッドのC<where>オプションではハッシュの代わりに
356
+タグを利用することもできます。これによって柔軟な
357
+条件を指定することができます。
358
+
359
+    # Select, more flexible where
360
+    my $result = $dbi->select(
361
+        table  => 'book',
362
+        where  => ['{= author} and {like title}', 
363
+                   {author => 'Ken', title => '%Perl%'}]
364
+    );
365
+
366
+タグについては以降で解説します。
367
+
368
+=head2 3. 行のフェッチ
369
+
370
+C<select()>メソッドの戻り値であるL<DBIx::Custom::Result>
371
+には行をフェッチするためのさまざまなメソッドが
372
+用意されています。
373
+(このセクションの解説では「配列」は「配列のリファレンス」を
374
+「ハッシュ」は「ハッシュのリファレンス」を意味しますので
375
+注意してください。)
376
+
377
+=head3 C<fetch>
378
+
379
+一行フェッチして配列に格納します。
380
+
381
+    while (my $row = $result->fetch) {
382
+        my $author = $row->[0];
383
+        my $title  = $row->[1];
384
+    }
385
+
386
+=head3 C<fetch_first>
387
+
388
+一行だけフェッチして配列に格納します。
389
+
390
+    my $row = $result->fetch_first;
391
+
392
+フェッチが終わった後は、ステートメントハンドルからC<finish()>
393
+メソッドが呼び出されてそれ以上フェッチできなくなります。
394
+
395
+=head3 C<fetch_multi>
396
+
397
+複数行をフェッチして配列の配列に格納します。
398
+
399
+    while (my $rows = $result->fetch_multi(5)) {
400
+        my $first_author  = $rows->[0][0];
401
+        my $first_title   = $rows->[0][1];
402
+        my $second_author = $rows->[1][0];
403
+        my $second_value  = $rows->[1][1];
404
+    }
405
+
406
+=head3 C<fetch_all>
407
+
408
+すべての行をフェッチして配列の配列に格納します。
409
+
410
+    my $rows = $result->fetch_all;
411
+
412
+=head3 C<fetch_hash>
413
+
414
+一行フェッチしてハッシュに格納します。
415
+
416
+    while (my $row = $result->fetch_hash) {
417
+        my $title  = $row->{title};
418
+        my $author = $row->{author};
419
+    }
420
+
421
+=head3 C<fetch_hash_first>
422
+
423
+一行だけフェッチしてハッシュに格納します。
424
+
425
+    my $row = $result->fetch_hash_first;
426
+
427
+フェッチが終わった後は、ステートメントハンドルからC<finish()>
428
+メソッドが呼び出されてそれ以上フェッチできなくなります。
429
+
430
+=head3 C<fetch_hash_multi>
431
+
432
+複数行をフェッチしてハッシュの配列に格納します。
433
+
434
+    while (my $rows = $result->fetch_hash_multi(5)) {
435
+        my $first_title   = $rows->[0]{title};
436
+        my $first_author  = $rows->[0]{author};
437
+        my $second_title  = $rows->[1]{title};
438
+        my $second_author = $rows->[1]{author};
439
+    }
440
+
441
+=head3 C<fetch_all>
442
+
443
+すべての行をフェッチしてハッシュの配列に格納します。
444
+
445
+    my $rows = $result->fetch_hash_all;
446
+
447
+L<DBI>のステートメントハンドルに直接アクセスしたい場合は
448
+<sth>を使用します。
449
+
450
+    my $sth = $result->sth;
451
+
452
+=head2 4. ハッシュパラメタバインド
453
+
454
+L<DBIx::Custom>はハッシュパラメタバインドを提供します。
455
+
456
+まず最初にL<DBI>による通常のパラメタバインドをご覧ください。
457
+
458
+    use DBI;
459
+    my $dbh = DBI->connect(...);
460
+    my $sth = $dbh->prepare(
461
+        "select * from book where author = ? and title like ?;"
462
+    );
463
+    $sth->execute('Ken', '%Perl%');
464
+
465
+これはデータベースシステムがSQLをキャッシュすることができ、
466
+パラメータは自動的にクォートされるので、
467
+パフォーマンス面でも、セキュリティ面でも
468
+とても良い方法です。
469
+
470
+
471
+L<DBIx::Custom>はこれを改善して、ハッシュで
472
+パラメタを指定できるようにしました。
473
+
474
+    my $result = $dbi->execute(
475
+        "select * from book where {= author} and {like title};"
476
+        param => {author => 'Ken', title => '%Perl%'}
477
+    );
478
+
479
+C<{= author}>とC<{like title}>はタグと呼ばれます。
480
+タグは内部ではプレースホルダを含む文字列に置き換えられます。
481
+
482
+    select * from book where {= author} and {like title}
483
+
484
+という文は以下のSQLに置き換えられます。
485
+
486
+    select * from book where author = ? and title like ?;
487
+
488
+このようにタグを使ってSQL文を表現するのがL<DBIx::Custom>の
489
+特徴です。以下のタグが利用可能です。
490
+
491
+    [TAG]                       [REPLACED]
492
+    {? NAME}               ->   ?
493
+    {= NAME}               ->   NAME = ?
494
+    {<> NAME}              ->   NAME <> ?
495
+    
496
+    {< NAME}               ->   NAME < ?
497
+    {> NAME}               ->   NAME > ?
498
+    {>= NAME}              ->   NAME >= ?
499
+    {<= NAME}              ->   NAME <= ?
500
+    
501
+    {like NAME}            ->   NAME like ?
502
+    {in NAME COUNT}        ->   NAME in [?, ?, ..]
503
+    
504
+    {insert_param NAME1 NAME2}   ->   (NAME1, NAME2) values (?, ?)
505
+    {update_param NAME1 NAME2}   ->   set NAME1 = ?, NAME2 = ?
506
+
507
+これらの変換はL<DBIx::Custom::QueryBuilder>によって行われます。
508
+
509
+C<{>とC<}>は予約語です。これらの文字を使いたい場合は
510
+「\」を使ってエスケープする必要があります。
511
+'\'はPerlのエスケープ文字なので、
512
+エスケープするためには'\\'と書く必要があることに注意
513
+してください。
514
+
515
+    'select * from book \\{ something statement \\}'
516
+
517
+=head2 5. フィルタリング
518
+
519
+=head3 パラメタバインド時のフィルタリング
520
+
521
+データベースに登録するデータをフィルタリングしたい場合
522
+があります。たとえば、内部文字列で文字列を保持している場合は
523
+データベースにデータを登録する前に、バイト文字列に変換する
524
+必要があります。L<DBIx::Custom>のフィルタリングシステムは
525
+あるデータを他のデータに変換するのを手助けしてくれます。
526
+
527
+フィルタリングを利用するにはまず、
528
+C<register_filter()>メソッドを使用して
529
+フィルタを登録しておく必要があります。
530
+
531
+    $dbi->register_filter(
532
+        to_upper_case => sub {
533
+            my $value = shift;
534
+            return uc $value;
535
+        }
536
+    );
537
+
538
+デフォルトのフィルタとしてC<encode_utf8>とC<decode_utf8>
539
+が登録されています。
540
+
541
+登録されているフィルタはC<execute()>メソッドのC<filter>オプション
542
+で指定することができます。
543
+
544
+    my $result = $dbi->execute(
545
+        "select * from book where {= author} and {like title};"
546
+        param  => {author => 'Ken', title => '%Perl%'},
547
+        filter => {author => 'to_upper_case, title => 'encode_utf8'}
548
+    );
549
+
550
+この例ではC<author>の値はバインドされるときに大文字に変換され、
551
+C<title>の値はバイト文字列に変換されます。
552
+
553
+C<filter>オプションは
554
+C<insert()>、C<update()>、 C<update_all()>,
555
+C<delete()>、C<select()>
556
+メソッドにおいても使用することができます。
557
+
558
+    # insert() with filter option
559
+    $dbi->insert(table  => 'book',
560
+                 param  => {title => 'Perl', author => 'Ken'},
561
+                 filter => {title => 'encode_utf8'});
562
+    
563
+    # select() with filter option
564
+    my $result = $dbi->select(
565
+        table  => 'book',
566
+        column => [qw/author title/],
567
+        where  => {author => 'Ken'},
568
+        append => 'order by id limit 1',
569
+        filter => {title => 'encode_utf8'}
570
+    );
571
+
572
+B<フィルタのサンプル>
573
+
574
+MySQL
575
+
576
+    # Time::Piece object to DATETIME format
577
+    tp_to_datetime => sub {
578
+        return shift->strftime('%Y-%m-%d %H:%M:%S');
579
+    }
580
+    
581
+    # Time::Piece object to DATE format
582
+    tp_to_date => sub {
583
+        return shift->strftime('%Y-%m-%d');
584
+    }
585
+    
586
+    # DATETIME to Time::Piece object
587
+    datetime_to_tp => sub {
588
+        return Time::Piece->strptime(shift, '%Y-%m-%d %H:%M:%S');
589
+    }
590
+    
591
+    # DATE to Time::Piece object
592
+    date_to_tp => sub {
593
+        return Time::Piece->strptime(shift, '%Y-%m-%d');
594
+    }
595
+
596
+SQLite
597
+    
598
+    # Time::Piece object to DATETIME format
599
+    tp_to_datetime => sub {
600
+        return shift->strftime('%Y-%m-%d %H:%M:%S');
601
+    }
602
+    
603
+    # Time::Piece object to DATE format
604
+    tp_to_date => sub {
605
+        return shift->strftime('%Y-%m-%d');
606
+    }
607
+    
608
+    # DATETIME to Time::Piece object
609
+    datetime_to_tp => sub {
610
+        return Time::Piece->strptime(shift, '%Y-%m-%d %H:%M:%S');
611
+    }
612
+    
613
+    # DATE to Time::Piece object
614
+    date_to_tp => sub {
615
+        return Time::Piece->strptime(shift, '%Y-%m-%d');
616
+    }
617
+
618
+=head3 行のフェッチ時のフィルタリング
619
+
620
+行をフェッチするときのフィルタも設定することができます。
621
+これはL<DBIx::Custom::Result>クラスのC<filter>メソッドを使って
622
+行います。
623
+
624
+    my $result = $dbi->select(table => 'book');
625
+    $result->filter({title => 'decode_utf8', author => 'to_upper_case'});
626
+
627
+フェッチのためのフィルタにおいて、
628
+たとえ、列名が大文字を含む場合であっても
629
+列名は小文字であることに注意してください。
630
+これはデータベースシステムに依存させないための要件です。
631
+
632
+=head2 6. パフォーマンスの改善
633
+
634
+=head3 シュガーメソッドを使わない
635
+
636
+もしC<insert()>メソッドを使用してインサートを実行した場合、
637
+必要なパフォーマンスを得られない場合があるかもしれません。
638
+C<insert()>メソッドは、SQL文とステートメントハンドルを
639
+毎回作成するためすこし遅いです。
640
+
641
+そのような場合は、C<create_query()>メソッドによって
642
+クエリを用意しておくことができます。
643
+    
644
+    my $query = $dbi->create_query(
645
+        "insert into book {insert_param title author};"
646
+    );
647
+
648
+戻り値はL<DBIx::Custom::Query>オブジェクトです。
649
+このオブジェクトはSQL文とパラメータバインド時の列名を
650
+保持しています。またステートメントハンドルも保持しています。
651
+
652
+    {
653
+        sql     => 'insert into book (title, author) values (?, ?);',
654
+        columns => ['title', 'author'],
655
+        sth     => $sth
656
+    }
657
+
658
+クエリオブジェクトを使って繰り返し実行するには次のようにします。
659
+    
660
+    my $inputs = [
661
+        {title => 'Perl',      author => 'Ken'},
662
+        {title => 'Good days', author => 'Mike'}
663
+    ];
664
+    
665
+    foreach my $input (@$inputs) {
666
+        $dbi->execute($query, $input);
667
+    }
668
+
669
+C<execute>メソッドの第一引数にクエリオブジェトを渡すことができます。
670
+これはC<insert()>メソッドよりも高速です。
671
+
672
+=head2 7. その他の機能
673
+
674
+=head3 トランザクション
675
+
676
+トランザクションを便利に利用するために、
677
+C<begin_work()>、C<commit()>、C<rollback()>
678
+という三つのメソッドが容易されています。
679
+これはL<DBI>の同名のメソッドと同じ機能を持ちます。
680
+
681
+    $dbi->begin_work;
682
+    
683
+    eval {
684
+        $dbi->update(...);
685
+        $dbi->update(...);
686
+    };
687
+    
688
+    if ($@) {
689
+        $dbi->rollback;
690
+    }
691
+    else {
692
+        $dbi->commit;
693
+    }
694
+
695
+=head3 selectメソッドの結果クラスの変更
696
+
697
+必要ならばC<select()>メソッドの結果クラスを変更することができます。
698
+
699
+    package Your::Result;
700
+    use base 'DBIx::Custom::Result';
701
+    
702
+    sub some_method { ... }
703
+
704
+    1;
705
+    
706
+    package main;
707
+    
708
+    use Your::Result;
709
+    
710
+    my $dbi = DBIx::Custom->connect(...);
711
+    $dbi->result_class('Your::Result');
712
+
713
+=head3 L<DBIx::Custom::QueryBuilder>の機能の拡張
714
+
715
+新しいタグが欲しい場合はL<DBIx::Custom::QueryBuilder>の機能を拡張
716
+することができます。
717
+
718
+    my $dbi = DBIx::Custom->connect(...);
719
+    $dbi->query_builder->register_tag_processor(
720
+        name => sub {
721
+           ...
722
+        }
723
+    );
724
+
725
+=head3 ヘルパーメソッドの登録
726
+
727
+ヘルパーメソッドを登録することができます。
728
+
729
+    $dbi->helper(
730
+        update_or_insert => sub {
731
+            my $self = shift;
732
+            # do something
733
+        },
734
+        find_or_create   => sub {
735
+            my $self = shift;
736
+            # do something
737
+        }
738
+    );
739
+
740
+<helper()>メソッドで登録したメソッドは
741
+L<DBIx::Custom>オブジェクトから直接呼び出すことができます。
742
+
743
+    $dbi->update_or_insert;
744
+    $dbi->find_or_create;
745
+
746
+=head3 ユーティリティメソッド(実験的)
747
+
748
+C<expand>メソッドを使用すると次のようなハッシュに含まれる
749
+テーブル名と列名を結合することができます。
750
+
751
+    my %expanded = $dbi->expand(\%source);
752
+
753
+以下のハッシュ
754
+
755
+    {book => {title => 'Perl', author => 'Ken'}}
756
+
757
+は次のように展開されます。
758
+
759
+    ('book.title' => 'Perl', 'book.author' => 'Ken')
760
+
761
+これはテーブル名を含むselect文で利用すると便利です。
762
+
763
+    my $param = {title => 'Perl', author => '%Ken%'};
764
+    $dbi->execute(
765
+        'select * from book where {= book.title} && {like book.author};',
766
+        param => {$dbi->expand({book => $param})}
767
+    );
768
+
769
+=cut
+2
lib/DBIx/Custom/QueryBuilder.pm
... ...
@@ -11,6 +11,8 @@ use DBIx::Custom::QueryBuilder::TagProcessors;
11 11
 
12 12
 # Carp trust relationship
13 13
 push @DBIx::Custom::CARP_NOT, __PACKAGE__;
14
+push @DBIx::Custom::Where::CARP_NOT, __PACKAGE__;
15
+
14 16
 
15 17
 # Attributes
16 18
 __PACKAGE__->attr('tag_processors' => sub {
+3
lib/DBIx/Custom/Where.pm
... ...
@@ -10,6 +10,9 @@ use overload '""' => sub { shift->to_string }, fallback => 1;
10 10
 
11 11
 use Carp 'croak';
12 12
 
13
+# Carp trust relationship
14
+push @DBIx::Custom::CARP_NOT, __PACKAGE__;
15
+
13 16
 __PACKAGE__->attr(
14 17
   'query_builder',
15 18
   clause => sub { [] },