File Coverage

blib/lib/Rose/HTMLx/Form/Related/Metadata.pm
Criterion Covered Total %
statement 21 115 18.2
branch 0 60 0.0
condition 0 12 0.0
subroutine 7 30 23.3
pod 22 22 100.0
total 50 239 20.9


line stmt bran cond sub pod time code
1             package Rose::HTMLx::Form::Related::Metadata;
2 1     1   7 use strict;
  1         2  
  1         109  
3 1     1   7 use warnings;
  1         1  
  1         31  
4 1     1   5 use Carp;
  1         2  
  1         66  
5 1     1   1344 use Data::Dump qw( dump );
  1         9140  
  1         91  
6 1     1   13 use base qw( Rose::Object );
  1         2  
  1         108  
7              
8 1     1   1056 use Rose::HTMLx::Form::Related::RelInfo;
  1         4  
  1         164  
9              
10             our $VERSION = '0.24';
11              
12             use Rose::Object::MakeMethods::Generic (
13 1         10 'scalar' => [
14             qw( form relationship_data
15             related_fields related_field_names )
16             ],
17             'scalar --get_set_init' => [
18             'relationships', 'relinfo_class',
19             'object_class', 'labels',
20             'controller_prefix', 'field_uris',
21             'related_field_map', 'sort_prefix',
22             'default_sort_by', 'default_related_sort_by',
23             'default_selected', 'takes_object_as_argument',
24             'field_methods',
25             ],
26             'boolean --get_set' => [
27             'show_related_values' => { default => 1 },
28             'show_related_fields' => { default => 1 },
29             'show_relationships' => { default => 1 },
30             'interrelate_fields' => { default => 1 },
31             ],
32 1     1   6 );
  1         2  
33              
34             =head1 NAME
35              
36             Rose::HTMLx::Form::Related::Metadata - RHTMLO Form class metadata
37              
38             =head1 DESCRIPTION
39              
40             Rose::HTMLx::Form::Related::Metadata interrogates and caches interrelationships
41             between Form and ORM classes.
42              
43             You typically access an instance of this class via the metadata() method in
44             your Form class.
45              
46             =head1 METHODS
47              
48             =cut
49              
50             =head2 init
51              
52             Overrides base init() method to build metadata.
53              
54             =cut
55              
56             sub init {
57 0     0 1   my $self = shift;
58 0           $self->SUPER::init(@_);
59 0 0 0       if ( !defined $self->form
60             or !$self->form->isa('Rose::HTMLx::Form::Related') )
61             {
62 0           croak "Rose::HTMLx::Form::Related object required";
63             }
64 0           $self->_build;
65 0 0         if ( $self->form->debug ) {
66 0           dump $self;
67             }
68 0           return $self;
69             }
70              
71             =head2 form( [I
] )
72              
73             Get/set the Rose::HTMLx::Related::Form object.
74              
75             =head2 related_field_names([I])
76              
77             Get/set the array ref of field names representing foreign keys.
78              
79             =head2 related_fields( [I] )
80              
81             Get/set the hash ref of related field names to RelInfo objects.
82              
83             =head2 relationship_data( [I] )
84              
85             Get/set the hash ref of RelInfo objects, keyed by the RelInfo->name
86             value.
87              
88             =head2 show_related_fields
89              
90             Boolean indicating whether the Form should provide links to related
91             forms based on ORM relationships.
92              
93             Default is true.
94              
95             =head2 show_related_values
96              
97             Boolean indicating whether the Form should
98             show related unique field values rather than the foreign keys
99             to which they refer.
100              
101             Default is true.
102              
103             =head2 show_relationships
104              
105             Boolean indicating whether the View should provide links to related
106             tables based on RDBO relationship method names that do not have
107             corresponding field names.
108              
109             =cut
110              
111             =head2 init_controller_prefix
112              
113             The default is undef.
114              
115             =cut
116              
117 0     0 1   sub init_controller_prefix { return undef }
118              
119             =head2 init_labels
120              
121             Should return a hashref of method (field) names to labels. Useful for giving
122             labels to non-fields like relationship names.
123              
124             =cut
125              
126 0     0 1   sub init_labels { return {} }
127              
128             =head2 init_sort_prefix
129              
130             Should return a hashref of method (field) names to any strings that should
131             be prefixed to the name for sorting. This is to support (for example)
132             sorts on multi-table joins.
133              
134             Default is empty hashref.
135              
136             =cut
137              
138 0     0 1   sub init_sort_prefix { {} }
139              
140             =head2 init_object_class
141              
142             Should return the name of the ORM object class the Form class represents.
143             Default is the Form class name less the C<::Form> part.
144              
145             =cut
146              
147             sub init_object_class {
148 0     0 1   my $form_class = ref( shift->form );
149 0           $form_class =~ s/::Form$//;
150 0           return $form_class;
151             }
152              
153             =head2 init_field_uris
154              
155             Should return a hashref of field name to a URI value.
156              
157             =cut
158              
159             sub init_field_uris {
160 0     0 1   return {};
161             }
162              
163             =head2 init_default_sort_by
164              
165             Should return the name of the field to sort by in (for example)
166             search results.
167              
168             Default is null (empty string).
169              
170             =cut
171              
172 0     0 1   sub init_default_sort_by { return '' }
173              
174             =head2 init_default_related_sort_by
175              
176             Should return the name of the related field to sort by in (for
177             example) search results that join tables.
178              
179             Default is null (empty string).
180              
181             =cut
182              
183 0     0 1   sub init_default_related_sort_by { return '' }
184              
185             =head2 init_default_selected
186              
187             Should return the name of the relationship to show as initially
188             active in an interface.
189              
190             Default is null (emptry string).
191              
192             =cut
193              
194 0     0 1   sub init_default_selected { return '' }
195              
196             =head2 init_takes_object_as_argument
197              
198             Set hash ref of ORM method names in foreign_class
199             that take the related ORM object as a single argument.
200              
201             =cut
202              
203 0     0 1   sub init_takes_object_as_argument { return {} }
204              
205             =head2 field_uri( I )
206              
207             Returns the value from field_uris() for key I if such a key exists.
208             Otherwise, returns undef.
209              
210             =cut
211              
212             sub field_uri {
213 0     0 1   my $self = shift;
214 0 0         my $field_name = shift or croak "field_name required";
215 0 0         if ( exists $self->field_uris->{$field_name} ) {
216 0           return $self->field_uris->{$field_name};
217             }
218 0           return;
219             }
220              
221             =head2 init_field_methods
222              
223             Returns array of method names to use for rendering form. Default
224             is form->field_names().
225              
226             You may want to override this value, especially for large forms,
227             in order to show only a subset of the most meaningful field values.
228              
229             =cut
230              
231             sub init_field_methods {
232 0     0 1   my $self = shift;
233 0           return $self->form->field_names;
234             }
235              
236             =head2 init_related_field_map
237              
238             Used by show_related_fields_using(), this method should return a hashref
239             of I to I where I is a field in the Form
240             and I is a method name in the foreign object_class.
241              
242             The default is an empty hashref, which means that show_related_fields_using()
243             will take the first unique column it can find as the I.
244              
245             =cut
246              
247 0     0 1   sub init_related_field_map { return {} }
248              
249             =head2 init_relationships
250              
251             You may define the Form relationships as an array ref using this method in your
252             subclass, or via the "relationships" key/value pair in new().
253              
254             If you define this value explicitly, the value must be an array ref of
255             either Rose::HTMLx::Form::Related::RelInfo objects, or hash refs (which will
256             be blessed into Rose::HTMLx::Form::Related::RelInfo objects).
257              
258             If not defined,
259             discover_relationships() is automatically called internally in new().
260             The default return value is undef, triggering discover_relationships. You can
261             turn off relationships altogether if you set it to an empty array ref,
262             although that begs the question of why you are using Rose::HTMLx::Form::Related
263             in the first place.
264              
265             =cut
266              
267 0     0 1   sub init_relationships { }
268              
269             =head2 init_relinfo_class
270              
271             Returns the default value 'Rose::HTMLx::Form::Related::RelInfo'.
272              
273             =cut
274              
275 0     0 1   sub init_relinfo_class {'Rose::HTMLx::Form::Related::RelInfo'}
276              
277             sub _build {
278 0     0     my $self = shift;
279              
280 0           my %related_fields;
281             my %relationship_info;
282              
283 0 0         if ( !defined $self->relationships ) {
284 0           $self->discover_relationships;
285             }
286              
287 0 0         if ( ref( $self->relationships ) ne 'ARRAY' ) {
288 0           croak "relationships() should be an ARRAY reference";
289             }
290              
291 0           RELINFO: for my $relinfo ( @{ $self->relationships } ) {
  0            
292              
293 0 0         if ( ref($relinfo) eq 'HASH' ) {
294 0           $relinfo = bless( $relinfo, $self->relinfo_class );
295             }
296 0 0 0       if ( !ref($relinfo) or !$relinfo->isa( $self->relinfo_class ) ) {
297 0           croak "$relinfo is not a " . $self->relinfo_class . " object";
298             }
299              
300 0           $relationship_info{ $relinfo->name } = $relinfo;
301              
302             # skip unless explicitly defined as a FK
303             # so we don't get PKs and UKs in here by mistake
304 0 0 0       if ( $relinfo->type ne 'foreign key'
305             and $relinfo->type ne 'many to one' )
306             {
307 0           next RELINFO;
308             }
309              
310 0 0         if ( my $colmap = $relinfo->cmap ) {
311 0           $relinfo->foreign_column( {} );
312 0           FIELDNAME: for my $field_name ( @{ $self->form->field_names } ) {
  0            
313              
314             # skip unless it's in the column map
315 0 0         next unless exists $colmap->{$field_name};
316              
317             # avoid condition where o2m overrides a FK
318 0 0         next if exists $related_fields{$field_name};
319              
320             #warn
321             # "field_name $field_name is in cmap";
322              
323 0           $relinfo->foreign_column->{$field_name}
324             = $colmap->{$field_name};
325              
326 0           $related_fields{$field_name} = $relinfo;
327             }
328             }
329              
330             }
331              
332 0           $self->{related_fields} = \%related_fields;
333 0           $self->{relationship_data} = \%relationship_info;
334 0           $self->{related_field_names} = [ keys %related_fields ];
335              
336             }
337              
338             =head2 discover_relationships
339              
340             This method must be overriden by model-specific subclasses. The method
341             should interrogate object_class() and set the array ref of relinfo_class()
342             objects via the relationships() mutator method.
343             A Rose::DB::Object-derived object that is a subclass of
344             Rose::DBx::Garden::Catalyst::Object will have a C
345             method, which is to be used in determining the name of the Controller
346             class associated with related Forms. Specifically, the return value
347             of C will be stripped from the beginning of the
348             related Form's class name, and will be replaced with the value of
349             C if such is defined.
350              
351             =cut
352              
353             sub discover_relationships {
354 0   0 0 1   my $class = ref( $_[0] ) || $_[0];
355 0           croak "no relationships defined and discover_relationships() "
356             . "not implemented for class $class";
357             }
358              
359             =head2 is_related_field( I )
360              
361             Returns true if I is a related_field().
362              
363             =cut
364              
365             sub is_related_field {
366 0     0 1   my $self = shift;
367 0 0         my $field_name = shift or croak "field_name required";
368 0           return exists $self->{related_fields}->{$field_name};
369             }
370              
371             =head2 related_field( I )
372              
373             If I represents a foreign key or other relationship to a different
374             object class (and hence a different form class), then related_field() will
375             return a hashref with relationship summary information.
376              
377             If I does not represent a related class, will croak.
378              
379             =cut
380              
381             sub related_field {
382 0     0 1   my $self = shift;
383 0 0         my $field_name = shift or croak "field_name required";
384              
385 0 0         croak "'$field_name' is not a related field"
386             unless $self->is_related_field($field_name);
387              
388 0           return $self->{related_fields}->{$field_name};
389             }
390              
391             =head2 has_relationship_info( I )
392              
393             Returns true if I information is known.
394              
395             =cut
396              
397             sub has_relationship_info {
398 0     0 1   my $self = shift;
399 0 0         my $rel = shift or croak "relationship object required";
400 0 0         my $name = ref($rel) ? $rel->name : $rel;
401 0           return exists $self->{relationship_data}->{$name};
402             }
403              
404             =head2 relationship_info( I )
405              
406             Returns the same RelInfo object as related_field(),
407             only using a relationship object or name instead of a field name.
408              
409             =cut
410              
411             sub relationship_info {
412 0     0 1   my $self = shift;
413 0 0         my $rel = shift or croak "relationship object required";
414 0 0         my $name = ref($rel) ? $rel->name : $rel;
415              
416 0 0         croak "no info for relationship '$name'"
417             unless $self->has_relationship_info($name);
418              
419 0           return $self->{relationship_data}->{$name};
420             }
421              
422             =head2 show_related_field_using( I, I )
423              
424             Returns the name of a field to use for display from I
425             based on a relationship using I.
426              
427             This magic is best explained via example. Say you have a 'person' object
428             that is related to a 'user' object. The relationship is defined in the 'user'
429             object as:
430              
431             person_id => person.id
432            
433             where the id of the 'person' object is a related (foreign key) to the person_id
434             value of the user object. In a form display for the 'user',
435             you might want to display the name of the 'person' rather than the id,
436             so show_related_field_using() will look
437             up the first unique text field in the I
438             (in this case, the 'person' class) and return that field.
439              
440             my $field_name = $form->show_related_field_using( 'MyPerson', 'person_id' )
441            
442             And because it's a method, you can override show_related_field_using()
443             to perform different logic than simply looking up the first unique text key
444             in the I.
445              
446             If no matching field is found, returns undef.
447              
448             The default behaviour is to ignore I
449             altogether, deferring to related_field_map() if I
450             is defined there and returning undef otherwise.
451              
452             Override this method in a base class
453             that understands how to interrogate I.
454              
455             =cut
456              
457             sub show_related_field_using {
458 0     0 1   my $self = shift;
459 0 0         my $fclass = shift or croak "foreign_object_class required";
460 0 0         my $field = shift or croak "field_name required";
461              
462 0 0         if ( exists $self->related_field_map->{$field} ) {
463 0           return $self->related_field_map->{$field};
464             }
465              
466 0 0         if ( $fclass->can('unique_value') ) {
467 0           return 'unique_value';
468             }
469              
470 0           return undef;
471             }
472              
473             =head2 foreign_field_value( I, I )
474              
475             Returns the value from the foreign object related to I
476             for the foreign column related to I.
477              
478             Returns undef if (a) there is no
479             foreign field related to I or (b) if there is
480             no foreign object.
481              
482             Example:
483              
484             my $username = $form->foreign_field_value( 'email_address', $person );
485             # $username comes from a $user record related to $person
486              
487             =cut
488              
489             sub foreign_field_value {
490 0     0 1   my $self = shift;
491 0 0         my $field_name = shift or croak "field_name required";
492 0 0         my $object = shift or croak "data object required";
493 0 0         return unless $self->is_related_field($field_name);
494 0 0         my $info = $self->related_field($field_name) or return;
495 0           my $foreign_field
496             = $self->show_related_field_using( $info->{foreign_class},
497             $field_name );
498 0           my $method = $info->{method};
499 0           my $foreign_object = $object->$method;
500              
501 0 0         if ( defined $foreign_object ) {
502              
503             # special RDBOHelper and MoreHelpers method
504 0 0         if ( $foreign_object->can('unique_value') ) {
505 0           $foreign_field = 'unique_value';
506             }
507              
508 0           return $foreign_object->$foreign_field;
509             }
510             else {
511 0           return undef;
512             }
513             }
514              
515             1;
516              
517             __END__