File Coverage

blib/lib/DBI/Easy/Record.pm
Criterion Covered Total %
statement 119 160 74.3
branch 24 54 44.4
condition 20 56 35.7
subroutine 15 20 75.0
pod 0 13 0.0
total 178 303 58.7


line stmt bran cond sub pod time code
1             package DBI::Easy::Record;
2             # $Id: Record.pm,v 1.6 2009/07/20 18:00:08 apla Exp $
3              
4 6     6   90465 use Class::Easy;
  6         17  
  6         39  
5              
6 6     6   815 use DBI::Easy;
  6         13  
  6         47  
7 6     6   32 use base qw(DBI::Easy);
  6         1273  
  6         17042  
8              
9             our $wrapper = 1;
10              
11             sub _init {
12 61     61   103 my $class = shift;
13            
14 61         169 my $params;
15            
16 61 100 66     431 if (@_ == 1 && ref $_[0] && ref $_[0] eq 'HASH') {
      66        
17             # old school
18 18         66 $params->{field_values} = $_[0];
19             } else {
20 43         154 $params = {@_};
21             }
22            
23 61         195 return $params;
24             }
25              
26             sub save {
27 6     6 0 26 my $self = shift;
28            
29 6         8 my $result;
30            
31 6 50       29 return unless $self->field_values;
32            
33 6         62 my $pk_column = $self->_pk_column_;
34            
35 6 100 33     77 if ($pk_column and $pk_column ne '' and defined $self->column_values and $self->column_values->{$pk_column}) {
      66        
      66        
36             # try to update
37 2         168 $result = $self->update_by_pk;
38             } else {
39 4         50 $result = $self->create;
40             }
41            
42 6         44 return $result;
43             }
44              
45             sub fetched {
46 0 0   0 0 0 return 1 if defined shift->{field_values};
47             }
48              
49             # update by pk
50             sub update_by_pk {
51 3     3 0 7 my $self = shift;
52 3         11 my %params = @_;
53            
54             # there we make decision:
55             # a) programmmer can provide update values
56             # we simply reject field values
57             # b) field_values already contains update values
58            
59 3         5 my $column_values;
60            
61 3 50 33     24 if (exists $params{set} and ref $params{set} and ref $params{set} eq 'HASH') {
      33        
62 0         0 $column_values = $self->fields_to_columns ($params{set});
63             } else {
64 3         17 $column_values = $self->fields_to_columns;
65             }
66            
67 3         33 my ($sql, $bind) = $self->sql_update_by_pk (%params);
68            
69 3 50       11 return unless defined $sql;
70            
71 3   33     55 debug "sql: $sql => " . (defined $bind and scalar @$bind ? join ', ', @$bind : '[]');
72            
73 3         281 my $result = $self->no_fetch ($sql, $bind);
74            
75 3         37 foreach my $k (keys %$column_values) {
76 3         167 $self->column_values->{$k} = $column_values->{$k};
77             }
78              
79 3         74 delete $self->{field_values};
80            
81 3         23 return $result
82             }
83              
84             # delete by pk
85             sub delete_by_pk {
86 1     1 0 4 my $self = shift;
87            
88 1         17 my ($sql, $bind) = $self->sql_delete_by_pk (@_);
89            
90 1   33     24 debug "sql: $sql => " . (defined $bind and scalar @$bind ? join ', ', @$bind : '[]');
91            
92 1         101 return $self->no_fetch ($sql, $bind);
93            
94             }
95              
96             sub create {
97 16     16 0 3052 my $self = shift;
98            
99 16         74 my $t = timer ('fields to columns translation');
100            
101 16         804 my $column_values = $self->fields_to_columns;
102            
103 16         73 $t->lap ('sql generation');
104            
105 16         248 my ($sql, $bind) = $self->sql_insert ($column_values);
106            
107 16   33     199 debug "sql: $sql => " . (defined $bind and scalar @$bind ? join ', ', @$bind : '[]');
108            
109 16         1566 $t->lap ('insert');
110            
111             # sequence is available for oracle insertions
112 16         129 my $pk_col = $self->_pk_column_;
113 16         89 my $seq;
114            
115 16 50 33     129 if ($pk_col and exists $column_values->{"_$pk_col"} and $column_values->{"_$pk_col"} =~ /^\s*(\w+)\.nextval\s*$/si) {
      33        
116 0         0 $seq = $1;
117             }
118              
119 16         237 my $id = $self->no_fetch ($sql, $bind, $seq);
120            
121 16         298 $t->lap ('perl wrapper for id');
122            
123 16 50       198 return unless defined $id;
124            
125 16         112 delete $self->{field_values};
126 16         65 $self->{column_values} = $column_values;
127            
128 16 50       70 return $id if $id =~ /^0E\d+$/;
129            
130 16 50       86 $self->{column_values}->{$pk_col} = $id
131             if $pk_col; # sometimes no primary keys in table
132              
133 16         241 $t->end;
134            
135 16         179 $t->total;
136            
137 16         199 return 1;
138             }
139              
140             sub fetch {
141 7     7 0 20 my $class = shift;
142 7         15 my $params = shift;
143 7         15 my $cols = shift;
144            
145 7         58 my $prefixed_params = $class->fields_to_columns ($params);
146            
147 7         617 my ($statement, $bind) = $class->sql_select (where => $prefixed_params, fieldset => $cols);
148            
149 7         60 debug "sql: '$statement'";
150            
151 7         746 my $record = $class->fetch_row ($statement, $bind);
152            
153             return
154 7 100       37 unless ref $record;
155            
156 4         26 return $class->new (
157             column_values => $record
158             );
159            
160             }
161              
162             sub fetch_or_create {
163 2     2 0 77 my $class = shift;
164 2         6 my $params = shift;
165            
166 2         70 my $record = $class->fetch ($params);
167            
168 2 50       9 unless (defined $record) {
169 2         26 $record = $class->new ($params);
170 2         155 $record->create;
171             }
172            
173 2         18 return $record;
174             }
175              
176             sub hash {
177 6     6 0 23388 my $self = shift;
178            
179 6         17 my $result = {};
180            
181             # we need to return everything we got from db + changes
182 1         6 my $result = {map {$_ => $self->{field_values}->{$_}}
  30         144  
183 6         44 grep {defined $self->{field_values}->{$_}}
184 6         15 keys %{$self->fields}};
185            
186 6         14 foreach my $col_name (keys %{$self->{column_values}}) {
  6         129  
187 21         73 my $col_meta = $self->columns->{$col_name};
188 21         143 my $col_value = $self->{column_values}->{$col_name};
189            
190 21 100       59 next unless defined $col_value;
191            
192 18 0 33     40 $result->{$col_name} = $col_value, next
193             if ! defined $col_meta and ! exists $result->{$col_meta->{field_name}};
194            
195 18 50       129 $result->{$col_meta->{field_name}} = (
    100          
196             exists $col_meta->{decoder} ? $col_meta->{decoder}->($self): $col_value
197             ) if ! exists $result->{$col_meta->{field_name}};
198             }
199            
200 6         16 return {%{$self->{embed}}, %$result};
  6         67  
201             }
202              
203             *TO_JSON = \&hash;
204             *TO_XML = \&hash;
205              
206             sub embed {
207 1     1 0 3 my $self = shift;
208 1         3 my $what = shift;
209            
210 1 50       6 if (@_ == 1) {
    0          
211 1 50       6 die "cannot embed '$what' into ". ref $self
212             if exists $self->fields->{$what};
213 1         11 $self->{embed}->{$what} = $_[0];
214             } elsif (@_ > 1) {
215 0         0 die "too many parameters";
216             }
217            
218 1         4 return $self->{embed}->{$what};
219            
220             }
221              
222             # example usage: $domain->is_related_to ('contacts', {
223             # isa => 'My::Entity::Contact::Collection',
224             # relation => [domain_key => domain_key_in_contacts], # optional, by default natural join
225             # many_to_many => 'My::Entity::Domain_Contact::Collection',
226             # filter => {}
227             # });
228              
229             # памятка использования is_related_to
230             #$ref->is_related_to (
231             # ‘entity’, # название сущности, доступной у объекта
232             # # после вызова этого метода
233             # ‘entity_pack’, # имя класса, корое используется в
234             # # качестве фабрики для сущностей
235             # filter => {}, # хэш фильтров для ограничения выборки
236             # relation => ['key_in_ref', 'key_in_entity'] # отношение
237             #);
238              
239             sub is_related_to {
240 4     4 0 134 my $ref = shift;
241 4         9 my $entity = shift;
242 4         12 my $pack = shift;
243 4         11 my %params = @_;
244              
245 4         19 my $t = timer ('all');
246            
247 4         9077 debug "$entity";
248            
249 4   50     752 my $filter = $params{filter} || {};
250            
251 4 50       23 $params{relation} = []
252             unless defined $params{relation};
253            
254 4   33     255 my $column = $params{relation}->[0] || $ref->_pk_;
255 4   33     903 my $ref_column = $params{relation}->[1] || ($ref->column_prefix
256             ? $ref->column_prefix
257             : $ref->table_name . '_'
258             ) . $column;
259            
260 4         67 try_to_use ($pack);
261            
262             # warn "column $column from table ".$ref->table_name." is related to column $ref_column from table ". $pack->table_name;
263            
264 4         2080 my $sub;
265             my $ref_sub;
266            
267            
268 4 100       27 if ($pack->is_collection) {
269             $sub = sub {
270 4     4   299 my $self = shift;
271            
272 4         38 return $pack->new ({filter => {%$filter, $ref_column => $self->$column}});
273 2         23 };
274             $ref_sub = sub {
275 0     0   0 my $self = shift;
276            
277 0         0 return $pack->new ({filter => {%$filter, $ref_column => $self->$column}});
278 2         11 };
279             } else {
280            
281             $sub = sub {
282 2     2   23606 my $self = shift;
283            
284 2         25 return $pack->fetch_or_create ({%$filter, $ref_column => $self->$column});
285 2         24 };
286             }
287            
288 4         1005 make_accessor ($ref, $entity, default => $sub);
289            
290 4         318 $t->end;
291             }
292              
293             sub validation_errors {
294 0     0 0   my $self = shift;
295            
296 0           my $errors = {};
297            
298 0           debug "field validation";
299            
300 0           foreach my $field (keys %{$self->fields}) {
  0            
301             # first, we need to validate throught db schema
302             # TODO
303 0           if (0) {
304             $errors->{$field} = 'schema-validation-error';
305             }
306             # second, we validate throught custom validators
307 0           my $method = "${field}_valid";
308 0 0         if ($self->can ($method)) {
309 0           debug "custom validation for $field";
310 0           my $error_code = $self->$method;
311 0 0         if ($error_code) {
312 0           $errors->{$field} = $error_code;
313 0           debug "failed: $error_code";
314             }
315             }
316             }
317            
318 0 0         return unless scalar keys %$errors;
319            
320 0           return $errors;
321             }
322              
323 0     0 0   sub dump_fields_exclude {
324             #TODO
325             }
326              
327             sub apply_request_params {
328 0     0 0   my $self = shift;
329 0           my $request = shift;
330            
331 0           foreach my $field (keys %{$self->fields}) {
  0            
332             # TODO: check for primary key. we don't want primary key value here
333 0           my $value = $request->param ($field);
334 0 0 0       next if !defined $value or $value eq '';
335 0           $self->{$field} = $value;
336             }
337              
338 0           my $values = {};
339            
340 0           foreach my $field (keys %{$self->columns}) {
  0            
341             # TODO: check for primary key. we don't want primary key value here
342 0           my $value = $request->param ($field);
343 0 0 0       next if !defined $value or $value eq '';
344 0           $values->{$field} = $value;
345             }
346            
347 0           my $fields = $self->columns_to_fields ($values);
348              
349 0           foreach my $field (keys %{$fields}) {
  0            
350 0           my $value = $fields->{$field};
351 0 0 0       next if !defined $value or $value eq '';
352 0           $self->{$field} = $value;
353             }
354             }
355              
356             1;