File Coverage

blib/lib/Class/DBI/FormTools.pm
Criterion Covered Total %
statement 87 87 100.0
branch 30 36 83.3
condition 16 20 80.0
subroutine 9 9 100.0
pod 3 3 100.0
total 145 155 93.5


line stmt bran cond sub pod time code
1             package Class::DBI::FormTools;
2              
3             our $VERSION = '0.000007';
4              
5 8     8   1324707 use strict;
  8         21  
  8         822  
6 8     8   51 use warnings;
  8         19  
  8         289  
7              
8 8     8   47 use Carp;
  8         19  
  8         722  
9              
10 8     8   12822 use HTML::Element;
  8         249272  
  8         63  
11              
12             sub form_fieldname
13             {
14 53     53 1 600564 my ($self,$accessor,$object_id,$remote_object_ids) = @_;
15              
16             # Get class name
17 53   66     218 my $class = ref $self || $self;
18              
19             # Set default values
20 53 100       152 $remote_object_ids = {} unless $remote_object_ids;
21              
22             # Check args based on how we are called
23 53 50 66     296 die join(qq{\n},
24             "When calling form_fieldname as a class method on $class,",
25             "an object id must be specified"
26             ) . "\n"
27             if !ref($self) && !defined($object_id);
28              
29 53         69 my %has_a_attributes;
30 53         72 foreach my $attr ( keys %{ $class->meta_info->{'has_a'} } ) {
  53         242  
31 51         787 $has_a_attributes{$attr}
32             = $class->meta_info->{'has_a'}->{$attr};
33             }
34              
35             ## Build primary key field
36 53         982 my $id_fields = {
37             %$remote_object_ids,
38             };
39              
40 53         83 my @id_fields;
41 53 100       133 if ( keys %$id_fields ) {
42 3         12 @id_fields = map { $_.'='.$id_fields->{$_} } keys %$id_fields;
  6         25  
43             }
44             else {
45 50 100       401 push @id_fields, ( ref($self) ) ? $self->id : 'new';
46             }
47              
48             # Compute fieldname
49 53   100     1993 my $fieldname = join(
50             '|',
51             'cdbi',
52             $object_id,
53             $class,
54             join(q{;},@id_fields),
55             $accessor || '',
56             );
57            
58 53         523 return($fieldname);
59             }
60              
61              
62             sub formdata_to_objects
63             {
64 7     7 1 9878 my ($self,$formdata) = @_;
65              
66            
67             # Mapping from new objects without id's to their new id
68             # A non existing object will have a negative id given to it by the gui
69             # e.g. if there are two event objects one will have -1 and the other
70             # will have -2 as id. Other objects may reference this negative id, and
71             # when the object is created for real (or at least when the id has been
72             # selected) the -1 can be replaced with the real value
73             # $idmapping->{$object_type}->{$negative_id} = $real_id
74 7         22 my $idmapping = {};
75              
76             # Extract all cdbi fields
77 7         30 my @cdbi_formkeys = grep { /^cdbi\|/ } keys %$formdata;
  41         151  
78              
79             # Create a todo list with one entry for each unique objects
80             # So we can process them in reverse order of dependency
81 7         24 my %todolist;
82              
83             # Sort data into piles for later object creation/updating
84             my $processes_data;
85 7         26 foreach my $formkey ( @cdbi_formkeys ) {
86 41         153 my ($prefix,$object_id,$class,$id,$attribute) = split(/\|/,$formkey);
87              
88             # Only store value if an attribute name exists
89             # N-M relations with no extra data in the mapping table will not have
90             # a attribute name defined. The form name will look something like
91             # this: 'cdbi|o3|Role|actor_id=o2;film_id=o1|' and the value will be
92             # discarded
93 41 100       224 $processes_data->{$class}->{$object_id}->{'raw'}->{$attribute}
94             = $formdata->{$formkey} if $attribute;
95 41         95 $processes_data->{$class}->{$object_id}->{'form_id'}
96             = $id;
97              
98             # Save class name and id in the todo list
99             # (hash used to avoid dupes)
100 41         213 $todolist{"$class|$object_id"} = {
101             class => $class,
102             object_id => $object_id,
103             };
104             }
105              
106             # Flatten todo hash into a todolist array
107 7         37 my @todolist = values %todolist;
108              
109             # Build objects from form data
110 7         23 my @objects;
111 7         42 foreach my $todo ( @todolist ) {
112 18         120 my $object = $self->_inflate_object(
113             $todo->{ 'object_id' },
114             $todo->{ 'class' },
115             $processes_data,
116             );
117 18         62 push(@objects,$object);
118             }
119            
120 7         135 return(@objects);
121             }
122              
123              
124             sub _inflate_object
125             {
126 27     27   121 my ($self,$object_id,$class,$processed_data) = @_;
127              
128             ## Get handle on object_id && attributes for the object
129 27         91 my $attributes = $processed_data->{$class}->{$object_id}->{'raw'};
130              
131             ## Create id field
132             # form_id consists of more than one id field
133 27         44 my %id_field;
134 27         74 my $form_id = $processed_data->{$class}->{$object_id}->{'form_id'};
135 27 100 100     660 if ( $form_id && $form_id =~ /;/ ) {
    100 100        
    100          
136 3         21 foreach my $field ( split(/;/,$form_id) ) {
137 6         27 my ($key,$value) = split(/=/,$field);
138 6         23 $id_field{$key} = $value;
139             }
140             }
141             # Single column id field
142             elsif ( $form_id && $form_id ne 'new' ) {
143 8         33 %id_field = ( id => $form_id );
144             }
145             # Fallback to object id (if form_id is missing, it is probably a has_a
146             # where the user didn't supply the foreign object as a input parameter)
147             elsif ( !$form_id ) {
148 1         5 %id_field = ( id => $object_id );
149             }
150              
151             ## Inflate has_a has_a references
152 27         50 my @has_a_references = values %{ $class->meta_info->{'has_a'} };
  27         132  
153 27         578 foreach my $has_a ( @has_a_references ) {
154 19         139 my $foreign_class = $has_a->foreign_class;
155 19         262 my $foreign_accessor = $has_a->accessor->accessor;
156 19   100     818 my $foreign_id = $processed_data
157             ->{$class}
158             ->{$object_id}
159             ->{'raw'}
160             ->{$foreign_accessor}
161             ||= $id_field{$foreign_accessor};
162              
163 19 100       110 next unless $foreign_id;
164              
165             # Inflate foreign object
166 9         93 my $foreign_object = $self->_inflate_object($foreign_id,
167             $foreign_class,
168             $processed_data,
169             );
170             # Store inflated object in id and attribute hash
171 9         35 $attributes->{$foreign_accessor} = $foreign_object;
172 9 100       61 $id_field{$foreign_accessor} = $foreign_object
173             if exists($id_field{$foreign_accessor});
174             }
175              
176             ## Fetch object
177              
178             # Is this object allready retrieved?
179 27         88 my $object = $processed_data->{$class}->{$object_id}->{'object'};
180              
181             # No object? - Fetch existing object from database, and store it
182 27 100       133 unless ( $object ) {
183             #warn("Fetching $class object");
184 19 100       492 $object = $class->retrieve(%id_field) if keys %id_field;
185 19         18390 $processed_data->{$class}->{$object_id}->{'object'} = $object;
186             }
187              
188             # Still no object?
189 27 100       755 unless ( $object ) {
190 12         168 $object = $class->create({
191             %id_field,
192             %$attributes,
193             });
194 12         466114 $processed_data->{$class}->{$object_id}->{'object'} = $object;
195             }
196              
197             # Store attributes
198 27         2569 foreach my $attr ( keys %$attributes ) {
199             # Skip Dummy columns
200 69 50       31297 next unless $attr;
201              
202 69         511 $object->set($attr => $processed_data
203             ->{$class}
204             ->{$object_id}
205             ->{'raw'}
206             ->{$attr});
207             }
208             #warn("<<< Inflated ".ref($object));
209 27         8036 return($object);
210             }
211              
212              
213             sub form_field
214             {
215 6     6 1 15 my ($self,$name,$type,$object_id,$options,$default) = @_;
216              
217 6 50       29 croak "Field '$name' does not exist for object ".ref($self)
218             unless $self->can($name);
219              
220 6         8 my $input;
221              
222 6 50 33     20 if ( $type eq 'text' || $type eq 'hidden' ) {
223 6         24 $input = $self->_form_field_common(
224             $name, $type, $object_id, $options, $default
225             );
226             }
227              
228 6         22 my $markup = $input->as_XML;
229 6         1299 chomp($markup);
230              
231 6         67 return($markup);
232             }
233              
234              
235             sub _form_field_common
236             {
237 6     6   14 my ($self,$name,$type,$object_id,$options,$default) = @_;
238              
239 6 50       34 my $value = defined($default) ? $default
    50          
240             : ref($self) ? $self->get($name)
241             : q{}
242             ;
243              
244 6         3265 my $input = HTML::Element->new(
245             'input',
246             name => $self->form_fieldname($name,$object_id),
247             value => $value,
248             type => $type,
249             );
250 6         265 return($input);
251             }
252              
253              
254              
255             1; # Magic true value required at end of module
256             __END__