File Coverage

blib/lib/DBIx/Class/Relationship/ManyToMany.pm
Criterion Covered Total %
statement 40 40 100.0
branch 16 18 88.8
condition n/a
subroutine 7 7 100.0
pod 0 1 0.0
total 63 66 95.4


line stmt bran cond sub pod time code
1             package # hide from PAUSE
2             DBIx::Class::Relationship::ManyToMany;
3              
4 312     312   123842 use strict;
  312         832  
  312         9292  
5 312     312   1697 use warnings;
  312         679  
  312         7817  
6              
7 312     312   1950 use DBIx::Class::Carp;
  312         801  
  312         1865  
8 312     312   1956 use DBIx::Class::_Util qw( quote_sub perlstring );
  312         752  
  312         20563  
9              
10             # FIXME - this should go away
11             # instead Carp::Skip should export usable keywords or something like that
12             my $unique_carper;
13 312     312   8050 BEGIN { $unique_carper = \&carp_unique }
14              
15 312     312   1810 use namespace::clean;
  312         697  
  312         1861  
16              
17             our %_pod_inherit_config =
18             (
19             class_map => { 'DBIx::Class::Relationship::ManyToMany' => 'DBIx::Class::Relationship' }
20             );
21              
22             sub many_to_many {
23 3664     3664 0 575247 my ($class, $meth, $rel, $f_rel, $rel_attrs) = @_;
24              
25 3664 50       14344 $class->throw_exception(
26             "missing relation in many-to-many"
27             ) unless $rel;
28              
29 3664 50       11917 $class->throw_exception(
30             "missing foreign relation in many-to-many"
31             ) unless $f_rel;
32              
33 3664         12183 my $add_meth = "add_to_${meth}";
34 3664         10397 my $remove_meth = "remove_from_${meth}";
35 3664         9819 my $set_meth = "set_${meth}";
36 3664         9956 my $rs_meth = "${meth}_rs";
37              
38 3664         11595 for ($add_meth, $remove_meth, $set_meth, $rs_meth) {
39 14656 100       175041 if ( $class->can ($_) ) {
40 2 100       24 carp (<<"EOW") unless $ENV{DBIC_OVERWRITE_HELPER_METHODS_OK};
41              
42             ***************************************************************************
43             The many-to-many relationship '$meth' is trying to create a utility method
44             called $_.
45             This will completely overwrite one such already existing method on class
46             $class.
47              
48             You almost certainly want to rename your method or the many-to-many
49             relationship, as the functionality of the original method will not be
50             accessible anymore.
51              
52             To disable this warning set to a true value the environment variable
53             DBIC_OVERWRITE_HELPER_METHODS_OK
54              
55             ***************************************************************************
56             EOW
57             }
58             }
59              
60             my @main_meth_qsub_args = (
61             {},
62             { attributes => [
63             'DBIC_method_is_indirect_sugar',
64 3664 100       12521 ( keys( %{$rel_attrs||{}} )
  3664 100       32591  
65             ? 'DBIC_method_is_m2m_sugar_with_attrs'
66             : 'DBIC_method_is_m2m_sugar'
67             ),
68             ] },
69             );
70              
71              
72 3664         37648 quote_sub "${class}::${meth}", sprintf( <<'EOC', $rs_meth ), @main_meth_qsub_args;
73             DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call;
74             shift->%s( @_ )->search;
75             EOC
76              
77              
78             my @extra_meth_qsub_args = (
79             {
80 3664 100       33880 '$rel_attrs' => \{ alias => $f_rel, %{ $rel_attrs||{} } },
81             '$carp_unique' => \$unique_carper,
82             },
83             { attributes => [
84             'DBIC_method_is_indirect_sugar',
85 3664 100       1472249 ( keys( %{$rel_attrs||{}} )
  3664 100       28506  
86             ? 'DBIC_method_is_m2m_extra_sugar_with_attrs'
87             : 'DBIC_method_is_m2m_extra_sugar'
88             ),
89             ] },
90             );
91              
92              
93 3664         23522 quote_sub "${class}::${rs_meth}", sprintf( <<'EOC', map { perlstring $_ } ( "${class}::${meth}", $rel, $f_rel ) ), @extra_meth_qsub_args;
  10992         28198  
94              
95             DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
96             and
97             # allow nested calls from our ->many_to_many, see comment below
98             ( (CORE::caller(1))[3] ne %s )
99             and
100             DBIx::Class::_Util::fail_on_internal_call;
101              
102             # this little horror is there replicating a deprecation from
103             # within search_rs() itself
104             shift->related_resultset( %s )
105             ->related_resultset( %s )
106             ->search_rs (
107             undef,
108             ( @_ > 1 and ref $_[-1] eq 'HASH' )
109             ? { %%$rel_attrs, %%{ pop @_ } }
110             : $rel_attrs
111             )->search_rs(@_)
112             ;
113             EOC
114              
115             # the above is the only indirect method, the 3 below have too much logic
116 3664         1503319 shift @{$extra_meth_qsub_args[1]{attributes}};
  3664         13997  
117              
118              
119 3664         51104 quote_sub "${class}::${add_meth}", sprintf( <<'EOC', $add_meth, $rel, $f_rel ), @extra_meth_qsub_args;
120              
121             ( @_ >= 2 and @_ <= 3 ) or $_[0]->throw_exception(
122             "'%1$s' expects an object or hashref to link to, and an optional hashref of link data"
123             );
124              
125             $_[0]->throw_exception(
126             "The optional link data supplied to '%1$s' is not a hashref (it was previously ignored)"
127             ) if $_[2] and ref $_[2] ne 'HASH';
128              
129             my( $self, $far_obj ) = @_;
130              
131             my $guard;
132              
133             # the API is always expected to return the far object, possibly
134             # creating it in the process
135             if( not defined Scalar::Util::blessed( $far_obj ) ) {
136              
137             $guard = $self->result_source->schema->storage->txn_scope_guard;
138              
139             # reify the hash into an actual object
140             $far_obj = $self->result_source
141             ->related_source( q{%2$s} )
142             ->related_source( q{%3$s} )
143             ->resultset
144             ->search_rs( undef, $rel_attrs )
145             ->find_or_create( $far_obj );
146             }
147              
148             my $link = $self->new_related(
149             q{%2$s},
150             $_[2] || {},
151             );
152              
153             $link->set_from_related( q{%3$s}, $far_obj );
154              
155             $link->insert();
156              
157             $guard->commit if $guard;
158              
159             $far_obj;
160             EOC
161              
162              
163 3664         1560468 quote_sub "${class}::${set_meth}", sprintf( <<'EOC', $set_meth, $add_meth, $rel, $f_rel ), @extra_meth_qsub_args;
164              
165             my $self = shift;
166              
167             my $set_to = ( ref $_[0] eq 'ARRAY' )
168             ? ( shift @_ )
169             : do {
170             $carp_unique->(
171             "Calling '%1$s' with a list of items to link to is deprecated, use an arrayref instead"
172             );
173              
174             # gobble up everything from @_ into a new arrayref
175             [ splice @_ ]
176             }
177             ;
178              
179             # make sure folks are not invoking a bizarre mix of deprecated and curent syntax
180             $self->throw_exception(
181             "'%1$s' expects an arrayref of objects or hashrefs to link to, and an optional hashref of link data"
182             ) if (
183             @_ > 1
184             or
185             ( defined $_[0] and ref $_[0] ne 'HASH' )
186             );
187              
188             my $guard;
189              
190             # there will only be a single delete() op, unless we have what to set to
191             $guard = $self->result_source->schema->storage->txn_scope_guard
192             if @$set_to;
193              
194             # if there is a where clause in the attributes, ensure we only delete
195             # rows that are within the where restriction
196             $self->related_resultset( q{%3$s} )
197             ->search_rs(
198             ( $rel_attrs->{where}
199             ? ( $rel_attrs->{where}, { join => q{%4$s} } )
200             : ()
201             )
202             )->delete;
203              
204             # add in the set rel objects
205             $self->%2$s(
206             $_,
207             @_, # at this point @_ is either empty or contains a lone link-data hash
208             ) for @$set_to;
209              
210             $guard->commit if $guard;
211             EOC
212              
213              
214             # the last method needs no captures - just kill it all with fire
215 3664         1500602 $extra_meth_qsub_args[0] = {};
216              
217              
218 3664         41241 quote_sub "${class}::${remove_meth}", sprintf( <<'EOC', $remove_meth, $rel, $f_rel ), @extra_meth_qsub_args;
219              
220             $_[0]->throw_exception("'%1$s' expects an object")
221             unless defined Scalar::Util::blessed( $_[1] );
222              
223             $_[0]->related_resultset( q{%2$s} )
224             ->search_rs( $_[1]->ident_condition( q{%3$s} ), { join => q{%3$s} } )
225             ->delete;
226             EOC
227              
228             }
229              
230             1;