File Coverage

blib/lib/Class/DBI/Frozen/301/Relationship/HasMany.pm
Criterion Covered Total %
statement 47 80 58.7
branch 10 32 31.2
condition 3 8 37.5
subroutine 10 14 71.4
pod 0 3 0.0
total 70 137 51.0


line stmt bran cond sub pod time code
1             package Class::DBI::Relationship::HasMany;
2              
3 24     24   208 use strict;
  24         42  
  24         844  
4 24     24   125 use warnings;
  24         44  
  24         675  
5              
6 24     24   117 use base 'Class::DBI::Relationship';
  24         43  
  24         60303  
7              
8             sub remap_arguments {
9 1     1 0 4 my ($proto, $class, $accessor, $f_class, $f_key, $args) = @_;
10              
11 1 50       6 return $class->_croak("has_many needs an accessor name") unless $accessor;
12 1 50       3 return $class->_croak("has_many needs a foreign class") unless $f_class;
13 1 50       22 $class->can($accessor)
14             and return $class->_carp("$accessor method already exists in $class\n");
15              
16 1         3 my @f_method = ();
17 1 50       6 if (ref $f_class eq "ARRAY") {
18 0         0 ($f_class, @f_method) = @$f_class;
19             }
20 1         10 $class->_require_class($f_class);
21              
22 1 50       4 if (ref $f_key eq "HASH") { # didn't supply f_key, this is really $args
23 0         0 $args = $f_key;
24 0         0 $f_key = "";
25             }
26              
27 1   33     8 $f_key ||= do {
28 1         13 my $meta = $f_class->meta_info('has_a');
29 1         3 my ($col) = grep $meta->{$_}->foreign_class eq $class, keys %$meta;
30 1 50       13 $col || $class->table_alias;
31             };
32              
33 1 50       83 if (ref $f_key eq "ARRAY") {
34 0 0       0 return $class->_croak("Multi-column foreign keys not supported")
35             if @$f_key > 1;
36 0         0 $f_key = $f_key->[0];
37             }
38              
39 1   50     7 $args ||= {};
40 1         4 $args->{mapping} = \@f_method;
41 1         4 $args->{foreign_key} = $f_key;
42 1   33     8 $args->{order_by} ||= $args->{sort}; # deprecated 0.96
43 1 50       3 warn "sort argumemt to has_many deprecated in favour of order_by"
44             if $args->{sort}; # deprecated 0.96
45              
46 1         8 return ($class, $accessor, $f_class, $args);
47             }
48              
49             sub _set_up_class_data {
50 1     1   2 my $self = shift;
51 1         8 $self->class->_extend_class_data(
52             __hasa_list => $self->foreign_class => $self->args->{foreign_key});
53 1         39 $self->SUPER::_set_up_class_data;
54             }
55              
56             sub triggers {
57 1     1 0 2 my $self = shift;
58 1 50       4 return if $self->args->{no_cascade_delete}; # undocumented and untestsd!
59             return (
60             before_delete => sub {
61 0     0   0 $self->foreign_class->search($self->args->{foreign_key} => shift->id)
62             ->delete_all;
63 1         18 });
64             }
65              
66             sub methods {
67 1     1 0 3 my $self = shift;
68 1         4 my $accessor = $self->accessor;
69             return (
70 1         12 $accessor => $self->_has_many_method,
71             "add_to_$accessor" => $self->_method_add_to,
72             );
73             }
74              
75             sub _method_add_to {
76 1     1   15 my $self = shift;
77 1         3 my $accessor = $self->accessor;
78             return sub {
79 0     0   0 my ($self, $data) = @_;
80 0 0       0 my $class = ref $self
81             or return $self->_croak("add_to_$accessor called as class method");
82 0 0       0 return $self->_croak("add_to_$accessor needs data")
83             unless ref $data eq "HASH";
84              
85 0         0 my $meta = $class->meta_info(has_many => $accessor);
86 0         0 my ($f_class, $f_key, $args) =
87             ($meta->foreign_class, $meta->args->{foreign_key}, $meta->args);
88 0         0 $data->{$f_key} = $self->id;
89 0         0 $f_class->create($data);
90 1         20 };
91             }
92              
93             sub _has_many_method {
94 1     1   3 my $self = shift;
95 1         4 my $run_search = $self->_hm_run_search;
96 1 50       2 my @mapping = @{ $self->args->{mapping} } or return $run_search;
  1         4  
97             return sub {
98 0 0   0   0 return $run_search->(@_)->set_mapping_method(@mapping)
99             unless wantarray;
100 0         0 my @ret = $run_search->(@_);
101 0         0 foreach my $meth (@mapping) { @ret = map $_->$meth(), @ret }
  0         0  
102 0         0 return @ret;
103             }
104 0         0 }
105              
106             sub _hm_run_search {
107 1     1   2 my $self = shift;
108 1         4 my ($class, $accessor) = ($self->class, $self->accessor);
109             return sub {
110 0     0     my ($self, @search_args) = @_;
111 0           my $meta = $class->meta_info(has_many => $accessor);
112 0           my ($f_class, $f_key, $args) =
113             ($meta->foreign_class, $meta->args->{foreign_key}, $meta->args);
114 0 0         if (ref $self) { # For $artist->cds
115 0           unshift @search_args, ($f_key => $self->id);
116 0 0         push @search_args, { order_by => $args->{order_by} }
117             if defined $args->{order_by};
118 0           return $f_class->search(@search_args);
119             } else { # For Artist->cds
120             # Cross-table join as class method
121             # This stuff is highly experimental and will probably change beyond
122             # recognition. Use at your own risk...
123 0           my %kv = @search_args;
124 0           my $query = Class::DBI::Query->new({ owner => $f_class });
125 0           $query->kings($class, $f_class);
126 0           $query->add_restriction(sprintf "%s.%s = %s.%s",
127             $f_class->table_alias, $f_key, $class->table_alias,
128             $class->primary_column);
129 0           $query->add_restriction("$_ = ?") for keys %kv;
130 0           my $sth = $query->run(values %kv);
131 0           return $f_class->sth_to_objects($sth);
132             }
133 1         21 };
134             }
135              
136             1;