... | ... |
@@ -1,3 +1,5 @@ |
1 |
+0.1639 |
|
2 |
+ added experimental not_exists() |
|
1 | 3 |
0.1638 |
2 | 4 |
table object call dbi object method if not found method. |
3 | 5 |
added experimental base_table attribute and removed experimental table_class attribute |
... | ... |
@@ -333,13 +333,13 @@ sub execute{ |
333 | 333 |
} |
334 | 334 |
$filter = {%$filter, %$f}; |
335 | 335 |
|
336 |
- # Create bind values |
|
337 |
- my $binds = $self->_build_binds($params, $query->columns, $filter); |
|
336 |
+ # Bind |
|
337 |
+ my $bind = $self->_bind($params, $query->columns, $filter); |
|
338 | 338 |
|
339 | 339 |
# Execute |
340 |
- my $sth = $query->sth; |
|
340 |
+ my $sth = $query->sth; |
|
341 | 341 |
my $affected; |
342 |
- eval {$affected = $sth->execute(@$binds)}; |
|
342 |
+ eval {$affected = $sth->execute(@$bind)}; |
|
343 | 343 |
$self->_croak($@, qq{. Following SQL is executed. "$query->{sql}"}) if $@; |
344 | 344 |
|
345 | 345 |
# Return resultset if select statement is executed |
... | ... |
@@ -470,6 +470,8 @@ sub new { |
470 | 470 |
return $self; |
471 | 471 |
} |
472 | 472 |
|
473 |
+sub not_exists { bless {}, 'DBIx::Custom::NotExists' } |
|
474 |
+ |
|
473 | 475 |
sub register_filter { |
474 | 476 |
my $invocant = shift; |
475 | 477 |
|
... | ... |
@@ -695,31 +697,47 @@ sub where { |
695 | 697 |
return DBIx::Custom::Where->new(query_builder => shift->query_builder) |
696 | 698 |
} |
697 | 699 |
|
698 |
-sub _build_binds { |
|
700 |
+sub _bind { |
|
699 | 701 |
my ($self, $params, $columns, $filter) = @_; |
700 | 702 |
|
701 | 703 |
# bind values |
702 |
- my @binds; |
|
704 |
+ my @bind; |
|
703 | 705 |
|
704 | 706 |
# Build bind values |
705 | 707 |
my $count = {}; |
708 |
+ my $not_exists = {}; |
|
706 | 709 |
foreach my $column (@$columns) { |
707 | 710 |
|
708 | 711 |
# Value |
709 |
- my $value = ref $params->{$column} eq 'ARRAY' |
|
710 |
- ? $params->{$column}->[$count->{$column} || 0] |
|
711 |
- : $params->{$column}; |
|
712 |
+ my $value; |
|
713 |
+ if(ref $params->{$column} eq 'ARRAY') { |
|
714 |
+ my $i = $count->{$column} || 0; |
|
715 |
+ $i += $not_exists->{$column} || 0; |
|
716 |
+ my $found; |
|
717 |
+ for (my $k = $i; $i < @{$params->{$column}}; $k++) { |
|
718 |
+ if (ref $params->{$column}->[$k] eq 'DBIx::Custom::NotExists') { |
|
719 |
+ $not_exists->{$column}++; |
|
720 |
+ } |
|
721 |
+ else { |
|
722 |
+ $value = $params->{$column}->[$k]; |
|
723 |
+ $found = 1; |
|
724 |
+ last |
|
725 |
+ } |
|
726 |
+ } |
|
727 |
+ next unless $found; |
|
728 |
+ } |
|
729 |
+ else { $value = $params->{$column} } |
|
712 | 730 |
|
713 | 731 |
# Filter |
714 | 732 |
my $f = $filter->{$column} || $self->{default_out_filter} || ''; |
715 | 733 |
|
716 |
- push @binds, $f ? $f->($value) : $value; |
|
734 |
+ push @bind, $f ? $f->($value) : $value; |
|
717 | 735 |
|
718 | 736 |
# Count up |
719 | 737 |
$count->{$column}++; |
720 | 738 |
} |
721 | 739 |
|
722 |
- return \@binds; |
|
740 |
+ return \@bind; |
|
723 | 741 |
} |
724 | 742 |
|
725 | 743 |
sub _croak { |
... | ... |
@@ -1094,24 +1112,6 @@ Arguments is same as C<delete> method, |
1094 | 1112 |
except that C<delete_all> don't have C<where> argument. |
1095 | 1113 |
Return value of C<delete_all()> is the count of affected rows. |
1096 | 1114 |
|
1097 |
-=head2 C<(experimental) method> |
|
1098 |
- |
|
1099 |
- $dbi->method( |
|
1100 |
- update_or_insert => sub { |
|
1101 |
- my $self = shift; |
|
1102 |
- # do something |
|
1103 |
- }, |
|
1104 |
- find_or_create => sub { |
|
1105 |
- my $self = shift; |
|
1106 |
- # do something |
|
1107 |
- } |
|
1108 |
- ); |
|
1109 |
- |
|
1110 |
-Register method. These method is called from L<DBIx::Custom> object directory. |
|
1111 |
- |
|
1112 |
- $dbi->update_or_insert; |
|
1113 |
- $dbi->find_or_create; |
|
1114 |
- |
|
1115 | 1115 |
=head2 C<insert> |
1116 | 1116 |
|
1117 | 1117 |
$dbi->insert(table => $table, |
... | ... |
@@ -1132,18 +1132,11 @@ default to 0. This is experimental. |
1132 | 1132 |
This is overwrites C<default_bind_filter>. |
1133 | 1133 |
Return value of C<insert()> is the count of affected rows. |
1134 | 1134 |
|
1135 |
-=head2 C<new> |
|
1136 |
- |
|
1137 |
- my $dbi = DBIx::Custom->connect(data_source => "dbi:mysql:database=dbname", |
|
1138 |
- user => 'ken', password => '!LFKD%$&'); |
|
1139 |
- |
|
1140 |
-Create a new L<DBIx::Custom> object. |
|
1141 |
- |
|
1142 | 1135 |
=head2 C<(experimental) each_column> |
1143 | 1136 |
|
1144 | 1137 |
$dbi->each_column( |
1145 | 1138 |
sub { |
1146 |
- my ($table, $column, $info) = @_; |
|
1139 |
+ my ($self, $table, $column, $info) = @_; |
|
1147 | 1140 |
|
1148 | 1141 |
my $type = $info->{TYPE_NAME}; |
1149 | 1142 |
|
... | ... |
@@ -1155,8 +1148,39 @@ Create a new L<DBIx::Custom> object. |
1155 | 1148 |
Get column informations from database. |
1156 | 1149 |
Argument is callback. |
1157 | 1150 |
You can do anything in callback. |
1158 |
-Callback receive three arguments, table name, column name and column |
|
1159 |
-information. |
|
1151 |
+Callback receive four arguments, dbi object, table name, |
|
1152 |
+column name and columninformation. |
|
1153 |
+ |
|
1154 |
+=head2 C<(experimental) method> |
|
1155 |
+ |
|
1156 |
+ $dbi->method( |
|
1157 |
+ update_or_insert => sub { |
|
1158 |
+ my $self = shift; |
|
1159 |
+ # do something |
|
1160 |
+ }, |
|
1161 |
+ find_or_create => sub { |
|
1162 |
+ my $self = shift; |
|
1163 |
+ # do something |
|
1164 |
+ } |
|
1165 |
+ ); |
|
1166 |
+ |
|
1167 |
+Register method. These method is called from L<DBIx::Custom> object directory. |
|
1168 |
+ |
|
1169 |
+ $dbi->update_or_insert; |
|
1170 |
+ $dbi->find_or_create; |
|
1171 |
+ |
|
1172 |
+=head2 C<new> |
|
1173 |
+ |
|
1174 |
+ my $dbi = DBIx::Custom->connect(data_source => "dbi:mysql:database=dbname", |
|
1175 |
+ user => 'ken', password => '!LFKD%$&'); |
|
1176 |
+ |
|
1177 |
+Create a new L<DBIx::Custom> object. |
|
1178 |
+ |
|
1179 |
+=head2 C<(experimental) not_exists> |
|
1180 |
+ |
|
1181 |
+ my $not_exists = $dbi->not_exists; |
|
1182 |
+ |
|
1183 |
+Get DBIx::Custom::NotExists object. |
|
1160 | 1184 |
|
1161 | 1185 |
=head2 C<register_filter> |
1162 | 1186 |
|
... | ... |
@@ -878,8 +878,96 @@ C<=>タグの場合は |
878 | 878 |
|
879 | 879 |
を返す必要があるということです。 |
880 | 880 |
|
881 |
+タグの実装の他のサンプルはL<DBIx::Custom::Tag>のソースコード |
|
882 |
+をご覧になってみてください。 |
|
883 |
+ |
|
881 | 884 |
=head2 6. Where句の動的な生成 |
882 | 885 |
|
886 |
+=head3 Where句の動的な生成 where() |
|
887 |
+ |
|
888 |
+複数の検索条件を指定して、検索を行いたい場合があります。 |
|
889 |
+次の3つのケースのwhere句を考えてみましょう。 |
|
890 |
+下記のようなwhere句が必要になります。 |
|
891 |
+ |
|
892 |
+titleの値だけで検索したい場合 |
|
893 |
+ |
|
894 |
+ where {= title} |
|
895 |
+ |
|
896 |
+authorの値だけで検索したい場合 |
|
897 |
+ |
|
898 |
+ where {= author} |
|
899 |
+ |
|
900 |
+titleとauthorの両方の値で検索したい場合 |
|
901 |
+ |
|
902 |
+ where {= title} and {=author} |
|
903 |
+ |
|
904 |
+L<DBIx::Custom>では動的なWhere句の生成をサポートしています。 |
|
905 |
+まずC<where()>でL<DBIx::Custom::Where>オブジェクトを生成します。 |
|
906 |
+ |
|
907 |
+ my $where = $dbi->where; |
|
908 |
+ |
|
909 |
+次にC<clause()>を使用してwhere句を記述します。 |
|
910 |
+ |
|
911 |
+ $where->clause( |
|
912 |
+ ['and', '{= title'}, '{= author}'] |
|
913 |
+ ); |
|
914 |
+ |
|
915 |
+clauseの指定方法は次のようになります。 |
|
916 |
+ |
|
917 |
+ ['or' あるいは 'and', タグ1, タグ2, タグ3] |
|
918 |
+ |
|
919 |
+第一引数にはorあるいはandを指定します。第二引数以降には |
|
920 |
+検索条件をタグを使って記述します。 |
|
921 |
+ |
|
922 |
+C<clause>の指定は入れ子にすることもでき、さらに複雑な条件 |
|
923 |
+を記述することもできます。 |
|
924 |
+ |
|
925 |
+ ['and', |
|
926 |
+ '{= title}', |
|
927 |
+ ['or', '{= author}', '{like date}'] |
|
928 |
+ ] |
|
929 |
+ |
|
930 |
+このようにC<clause>を設定した後にC<param>にパラメータを指定します。 |
|
931 |
+ |
|
932 |
+ my $param => {title => 'Perl'}; |
|
933 |
+ $where->param($param); |
|
934 |
+ |
|
935 |
+この例ではtitleだけがパラメータに含まれています。 |
|
936 |
+ |
|
937 |
+この後C<to_string()>を実行すると$paramに含まれるパラメータを満たす |
|
938 |
+where句を生成することができます。 |
|
939 |
+ |
|
940 |
+ my $where_clause = $where->to_string; |
|
941 |
+ |
|
942 |
+パラメータはtitleだけですので、次のようなwhere句が生成されます。 |
|
943 |
+ |
|
944 |
+ where {= title} |
|
945 |
+ |
|
946 |
+またD<DBIx::Custom>は文字列の評価をオーバーロードして、C<to_string()> |
|
947 |
+を呼び出すようにしていますので、次のようにしてwhere句を生成することも |
|
948 |
+できます。 |
|
949 |
+ |
|
950 |
+ my $where_clause = "$where"; |
|
951 |
+ |
|
952 |
+これはSQLの中にwhere句を埋め込むときにとても役立つ機能です。 |
|
953 |
+ |
|
954 |
+また同一の列名を持つ場合はパラメータを配列のリファレンスにしてください。 |
|
955 |
+ |
|
956 |
+[undef, undef] |
|
957 |
+ |
|
958 |
+[sub {'not exists'}, 1] |
|
959 |
+ |
|
960 |
+ |
|
961 |
+ |
|
962 |
+ |
|
963 |
+ |
|
964 |
+=head3 select()との連携 |
|
965 |
+ |
|
966 |
+ |
|
967 |
+ |
|
968 |
+=head3 execute()との連携 |
|
969 |
+ |
|
970 |
+ |
|
883 | 971 |
=head2 7. パフォーマンスの改善 |
884 | 972 |
|
885 | 973 |
=head3 シュガーメソッドを使わない |
... | ... |
@@ -72,7 +72,7 @@ sub _parse { |
72 | 72 |
|
73 | 73 |
# Column |
74 | 74 |
my $columns = $self->query_builder->build_query($clause)->columns; |
75 |
- croak qq{each tag contains one column name: tag "$clause"} |
|
75 |
+ croak qq{Each tag contains one column name: tag "$clause"} |
|
76 | 76 |
unless @$columns == 1; |
77 | 77 |
my $column = $columns->[0]; |
78 | 78 |
|
... | ... |
@@ -82,21 +82,24 @@ sub _parse { |
82 | 82 |
# Push |
83 | 83 |
my $param = $self->param; |
84 | 84 |
my $pushed; |
85 |
- if (defined $param) { |
|
85 |
+ if (ref $param eq 'HASH') { |
|
86 | 86 |
if (exists $param->{$column}) { |
87 | 87 |
if (ref $param->{$column} eq 'ARRAY') { |
88 |
- $pushed = 1 if exists $param->{$column}->[$count - 1]; |
|
89 |
- } |
|
88 |
+ $pushed = 1 |
|
89 |
+ if exists $param->{$column}->[$count - 1] |
|
90 |
+ && ref $param->{$column}->[$count - 1] ne 'DBIx::Custom::NotExists'; |
|
91 |
+ } |
|
90 | 92 |
elsif ($count == 1) { |
91 | 93 |
$pushed = 1; |
92 | 94 |
} |
93 | 95 |
} |
94 | 96 |
push @$where, $clause if $pushed; |
95 | 97 |
} |
96 |
- else { |
|
98 |
+ elsif (!defined $param) { |
|
97 | 99 |
push @$where, $clause; |
98 | 100 |
$pushed = 1; |
99 | 101 |
} |
102 |
+ else { croak "Parameter must be hash reference or undfined value" } |
|
100 | 103 |
|
101 | 104 |
return $pushed; |
102 | 105 |
} |
... | ... |
@@ -886,7 +886,7 @@ $where = $dbi->where |
886 | 886 |
$result = $dbi->select( |
887 | 887 |
table => 'table1', |
888 | 888 |
where => $where, |
889 |
-); |
|
889 |
+); |
|
890 | 890 |
$row = $result->fetch_hash_all; |
891 | 891 |
is_deeply($row, [{key1 => 1, key2 => 2}]); |
892 | 892 |
|
... | ... |
@@ -957,6 +957,82 @@ $where = $dbi->where |
957 | 957 |
eval{$where->to_string}; |
958 | 958 |
like($@, qr/one column/); |
959 | 959 |
|
960 |
+$where = $dbi->where |
|
961 |
+ ->clause('{= key1}') |
|
962 |
+ ->param([]); |
|
963 |
+eval{$where->to_string}; |
|
964 |
+like($@, qr/Parameter/); |
|
965 |
+ |
|
966 |
+$where = $dbi->where |
|
967 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
968 |
+ ->param({key1 => [$dbi->not_exists, 1, 3]}); |
|
969 |
+$result = $dbi->select( |
|
970 |
+ table => 'table1', |
|
971 |
+ where => $where, |
|
972 |
+); |
|
973 |
+$row = $result->fetch_hash_all; |
|
974 |
+is_deeply($row, [{key1 => 1, key2 => 2}, {key1 => 3, key2 => 4}], 'not_exists'); |
|
975 |
+ |
|
976 |
+$where = $dbi->where |
|
977 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
978 |
+ ->param({key1 => [1, $dbi->not_exists, 3]}); |
|
979 |
+$result = $dbi->select( |
|
980 |
+ table => 'table1', |
|
981 |
+ where => $where, |
|
982 |
+); |
|
983 |
+$row = $result->fetch_hash_all; |
|
984 |
+is_deeply($row, [{key1 => 1, key2 => 2}, {key1 => 3, key2 => 4}], 'not_exists'); |
|
985 |
+ |
|
986 |
+$where = $dbi->where |
|
987 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
988 |
+ ->param({key1 => [1, 3, $dbi->not_exists]}); |
|
989 |
+$result = $dbi->select( |
|
990 |
+ table => 'table1', |
|
991 |
+ where => $where, |
|
992 |
+); |
|
993 |
+$row = $result->fetch_hash_all; |
|
994 |
+is_deeply($row, [{key1 => 1, key2 => 2}, {key1 => 3, key2 => 4}], 'not_exists'); |
|
995 |
+ |
|
996 |
+$where = $dbi->where |
|
997 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
998 |
+ ->param({key1 => [1, $dbi->not_exists, $dbi->not_exists]}); |
|
999 |
+$result = $dbi->select( |
|
1000 |
+ table => 'table1', |
|
1001 |
+ where => $where, |
|
1002 |
+); |
|
1003 |
+$row = $result->fetch_hash_all; |
|
1004 |
+is_deeply($row, [{key1 => 1, key2 => 2}], 'not_exists'); |
|
1005 |
+ |
|
1006 |
+$where = $dbi->where |
|
1007 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
1008 |
+ ->param({key1 => [$dbi->not_exists, 1, $dbi->not_exists]}); |
|
1009 |
+$result = $dbi->select( |
|
1010 |
+ table => 'table1', |
|
1011 |
+ where => $where, |
|
1012 |
+); |
|
1013 |
+$row = $result->fetch_hash_all; |
|
1014 |
+is_deeply($row, [{key1 => 1, key2 => 2}], 'not_exists'); |
|
1015 |
+ |
|
1016 |
+$where = $dbi->where |
|
1017 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
1018 |
+ ->param({key1 => [$dbi->not_exists, $dbi->not_exists, 1]}); |
|
1019 |
+$result = $dbi->select( |
|
1020 |
+ table => 'table1', |
|
1021 |
+ where => $where, |
|
1022 |
+); |
|
1023 |
+$row = $result->fetch_hash_all; |
|
1024 |
+is_deeply($row, [{key1 => 1, key2 => 2}], 'not_exists'); |
|
1025 |
+ |
|
1026 |
+$where = $dbi->where |
|
1027 |
+ ->clause(['or', ('{= key1}') x 3]) |
|
1028 |
+ ->param({key1 => [$dbi->not_exists, $dbi->not_exists, $dbi->not_exists]}); |
|
1029 |
+$result = $dbi->select( |
|
1030 |
+ table => 'table1', |
|
1031 |
+ where => $where, |
|
1032 |
+); |
|
1033 |
+$row = $result->fetch_hash_all; |
|
1034 |
+is_deeply($row, [{key1 => 1, key2 => 2}, {key1 => 3, key2 => 4}], 'not_exists'); |
|
1035 |
+ |
|
960 | 1036 |
test 'dbi_option default'; |
961 | 1037 |
$dbi = DBIx::Custom->new; |
962 | 1038 |
is_deeply($dbi->dbi_option, {}); |
... | ... |
@@ -1113,3 +1189,4 @@ $dbi->method( |
1113 | 1189 |
); |
1114 | 1190 |
is($dbi->base_table->one, 1, 'use dbi method'); |
1115 | 1191 |
is($dbi->table('table1')->one, 1, 'use dbi method'); |
1192 |
+ |