File Coverage

blib/lib/Class/DBI/DataMigration/Mapping/HasAToHasA.pm
Criterion Covered Total %
statement 21 28 75.0
branch 7 26 26.9
condition n/a
subroutine 4 4 100.0
pod 1 1 100.0
total 33 59 55.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             =head1 Name
4              
5             Class::DBI::DataMigration::Mapping::HasAToHasA - Map a single column in a
6             single row that represents a has_a relatsionship from the source database to a
7             single column in a single row that represents a has_a relationship in the
8             target database.
9              
10             =head1 Synopsis
11              
12             # Assume:
13             # - we have SourceDB and TargetDB, with two slightly different
14             # schemata for keeping track of cars
15             # - in the source Car class, there's a has_a relationship called
16             # 'body_colour' to a BodyColour object
17             # - in the target class, there's a has_a relationship called 'colour'
18             # to a Colour object
19             # - the 'name' field of a given Car's BodyColour should be used to find
20             # a Colour object in the target db, where the matching column is also 'name';
21             # this Colour object will be used to populate the has_a relationship in the
22             # target db
23              
24             my $mapping = Class::DBI::DataMigration::Mapping::HasAToHasA->new({
25             target_class => 'TargetDB::Colour',
26             target_class_search_key => 'name',
27             matching_source_key => 'body_colour->name'
28             });
29              
30             my $mapped_colour = $mapping->map('body_colour', $car);
31              
32             # ...$mapped_colour is now the Colour object in the target database that should
33             # be used to populate the has_a relationship there. See also the sample yaml file in
34             # Class::DBI::DataMigration::Migrator for an example of how this would be configured.
35              
36             =head1 Description
37              
38             A Class representing the mapping between a single column in a single row that
39             represents a has_a relatsionship from the source database to a single column in
40             a single row that represents a has_a relationship in the target database.
41              
42             =cut
43              
44 1     1   660146 use strict;
  1         2  
  1         49  
45              
46             package Class::DBI::DataMigration::Mapping::HasAToHasA;
47              
48 1     1   4 use base qw/Class::DBI::DataMigration::Mapping/;
  1         2  
  1         480  
49 1     1   5 use Carp;
  1         2  
  1         412  
50              
51             __PACKAGE__->mk_accessors(qw/
52             target_class
53             target_class_search_key
54             matching_source_key
55             target_allows_null
56             default_target_search_key_value
57             /);
58              
59             =head1 Methods
60              
61             =head2 target_class
62              
63             Accessor/mutator for the entity in which the target database object
64             representing our has_a releationship will be found.
65              
66             =head2 target_class_search_key
67              
68             Accessor/mutator for the key into the target entity class which should be used
69             to search for the object representing the has_a relationship.
70              
71             =head2 matching_source_key
72              
73             Accessor/mutator for the key into the source object we are mapping that should
74             be used to search for a matching value via the target class search key in the
75             target entity class.
76              
77             =head2 target_allows_null
78              
79             Accessor/mutator for a true or false value indicating whether it is an error
80             to be unable to find a matching object in the target has_a entity class; if set
81             to false, an error will be reported if no matching object is found at map()
82             time.
83              
84             =head2 default_target_search_key_value
85              
86             Accessor/mutator for a value which, if supplied, will be used as the default value
87             for searching in the target has_a class when no target has_a object can be found.
88              
89             =head2 map
90              
91             Given a primary key into our source entity, and an object from our source
92             class, attempt to find an object in the target has_a entity that matches the
93             object returned by calling our matching_source_key on the source object.
94              
95             If this search fails, and target_allows_null is false, we try again, using our
96             default_target_search_key_value, if it is defined. If we still haven't found an
97             object in the target database, we confess with an error.
98              
99             An error is also confessed if at any point we find more than one matching target
100             has_a object.
101              
102             =cut
103              
104             # subs:
105              
106             sub map {
107 1     1 1 51994 my ($self, $source_key, $source_object) = @_;
108              
109 1         4 my $source_class = ref $source_object;
110 1 50       58 eval "require $source_class" unless $source_class->can('new');
111 1 50       7 confess $@ if $@;
112 1 50       7 eval "require " . $self->target_class unless $self->target_class->can('new');
113 1 50       55 confess $@ if $@;
114              
115 1         7 my $value = eval 'return $source_object->' . $self->matching_source_key;
116 1 50       4383 confess $@ if $@;
117              
118 1         13 my @target_class_objs = $self->target_class->search(
119             $self->target_class_search_key => $value
120             );
121              
122              
123 1 50       1713 unless (@target_class_objs == 1) {
124              
125 0 0       0 if (@target_class_objs < 1) {
126 0 0       0 unless ($self->target_allows_null) {
127              
128 0 0       0 if (defined $self->default_target_search_key_value) {
129 0         0 @target_class_objs = $self->target_class->search(
130             $self->target_class_search_key =>
131             $self->default_target_search_key_value
132             );
133             }
134              
135             confess
136 0 0       0 'no target object or multiple target objects found in ' . $self->target_class .
    0          
137             ' for search key "' . $self->target_class_search_key .
138             '" with value "' . $value . '"' .
139             ($self->default_target_search_key_value ?
140             '-- even tried default value "' .
141             $self->default_target_search_key_value . '"' : '')
142             unless (@target_class_objs > 0);
143             }
144              
145             }
146              
147 0 0       0 if (@target_class_objs > 1) {
148 0         0 confess 'multiple target objects found in ' . $self->target_class . '
149             for search key "' . $self->target_class_search_key .
150             '" with value "' . $value . '"';
151             }
152              
153             } else {
154 1 50       9 Carp::Assert::should(ref($target_class_objs[0]), $self->target_class)
155             if $Carp::Assert::DEBUG;
156             }
157              
158 1         9 return $target_class_objs[0];
159             }
160              
161             =begin testing
162              
163             use lib 't/testlib';
164              
165             # loading this will dynamically set up the test source/target db's (but they
166             # will be empty; see below...):
167             use DMTestSetup;
168              
169             use_ok('Class::DBI::DataMigration::Mapping::HasAToHasA');
170             can_ok('Class::DBI::DataMigration::Mapping::HasAToHasA', 'map');
171              
172             # create sample data:
173              
174             my $source_grey = SourceDB::BodyColour->create({
175             name => 'grey'
176             });
177              
178             my $target_grey = TargetDB::Colour->create({
179             name => 'grey'
180             });
181              
182             my $car = SourceDB::Car->create({
183             make => 'Chevrolet',
184             model => 'Caprice Classic',
185             model_year => '1989',
186             body_colour => $source_grey
187             });
188              
189             # create & test a HasAToHasA mapping:
190              
191             my $mapping = Class::DBI::DataMigration::Mapping::HasAToHasA->new({
192             target_class => 'TargetDB::Colour',
193             target_class_search_key => 'name',
194             matching_source_key => 'body_colour->name'
195             });
196              
197             my $mapped_colour = $mapping->map('body_colour', $car);
198              
199             is($mapped_colour->name, $source_grey->name, 'Test mapping');
200              
201             =end testing
202              
203             =head1 See Also
204              
205             C
206              
207             =head1 Author
208              
209             Dan Friedman
210              
211             =head1 Copyright & License
212              
213             Copyright 2004 Dan Friedman, All Rights Reserved.
214              
215             This program is free software; you can redistribute it and/or modify it
216             under the same terms as Perl itself.
217              
218             Please note that these modules are not products of or supported by the
219             employers of the various contributors to the code.
220              
221             =cut
222              
223             1;
224              
225