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