| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 10 |  |  | 10 |  | 1695540 | use strict; | 
|  | 10 |  |  |  |  | 78 |  | 
|  | 10 |  |  |  |  | 406 |  | 
| 2 | 10 |  |  | 10 |  | 88 | use warnings; | 
|  | 10 |  |  |  |  | 40 |  | 
|  | 10 |  |  |  |  | 604 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | package DBIx::Class::ResultSet::RecursiveUpdate; | 
| 5 |  |  |  |  |  |  | $DBIx::Class::ResultSet::RecursiveUpdate::VERSION = '0.41'; | 
| 6 |  |  |  |  |  |  | # ABSTRACT: like update_or_create - but recursive | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 10 |  |  | 10 |  | 68 | use base qw(DBIx::Class::ResultSet); | 
|  | 10 |  |  |  |  | 29 |  | 
|  | 10 |  |  |  |  | 7458 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | sub recursive_update { | 
| 11 | 62 |  |  | 62 | 1 | 7545950 | my ( $self, $updates, $attrs ) = @_; | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 62 |  |  |  |  | 354 | my $fixed_fields; | 
| 14 |  |  |  |  |  |  | my $unknown_params_ok; | 
| 15 | 62 |  |  |  |  | 0 | my $m2m_force_set_rel; | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | # 0.21+ api | 
| 18 | 62 | 100 | 100 |  |  | 493 | if ( defined $attrs && ref $attrs eq 'HASH' ) { | 
|  |  | 100 | 66 |  |  |  |  | 
| 19 | 5 |  |  |  |  | 19 | $fixed_fields      = $attrs->{fixed_fields}; | 
| 20 | 5 |  |  |  |  | 11 | $unknown_params_ok = $attrs->{unknown_params_ok}; | 
| 21 | 5 |  |  |  |  | 13 | $m2m_force_set_rel = $attrs->{m2m_force_set_rel}; | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | # pre 0.21 api | 
| 25 |  |  |  |  |  |  | elsif ( defined $attrs && ref $attrs eq 'ARRAY' ) { | 
| 26 | 1 |  |  |  |  | 3 | $fixed_fields = $attrs; | 
| 27 |  |  |  |  |  |  | } | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 62 |  |  |  |  | 276 | return DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update( | 
| 30 |  |  |  |  |  |  | resultset         => $self, | 
| 31 |  |  |  |  |  |  | updates           => $updates, | 
| 32 |  |  |  |  |  |  | fixed_fields      => $fixed_fields, | 
| 33 |  |  |  |  |  |  | unknown_params_ok => $unknown_params_ok, | 
| 34 |  |  |  |  |  |  | m2m_force_set_rel => $m2m_force_set_rel, | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  | } | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | package DBIx::Class::ResultSet::RecursiveUpdate::Functions; | 
| 39 |  |  |  |  |  |  | $DBIx::Class::ResultSet::RecursiveUpdate::Functions::VERSION = '0.41'; | 
| 40 | 10 |  |  | 10 |  | 448941 | use Carp::Clan qw/^DBIx::Class|^HTML::FormHandler|^Try::Tiny/; | 
|  | 10 |  |  |  |  | 19698 |  | 
|  | 10 |  |  |  |  | 93 |  | 
| 41 | 10 |  |  | 10 |  | 1323 | use Scalar::Util qw( blessed ); | 
|  | 10 |  |  |  |  | 41 |  | 
|  | 10 |  |  |  |  | 658 |  | 
| 42 | 10 |  |  | 10 |  | 5956 | use List::MoreUtils qw/ any all none /; | 
|  | 10 |  |  |  |  | 122284 |  | 
|  | 10 |  |  |  |  | 83 |  | 
| 43 | 10 |  |  | 10 |  | 12476 | use Try::Tiny; | 
|  | 10 |  |  |  |  | 34 |  | 
|  | 10 |  |  |  |  | 563 |  | 
| 44 | 10 |  |  | 10 |  | 4660 | use Data::Dumper::Concise; | 
|  | 10 |  |  |  |  | 72959 |  | 
|  | 10 |  |  |  |  | 845 |  | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 10 |  |  | 10 |  | 114 | use constant DEBUG => 0; | 
|  | 10 |  |  |  |  | 35 |  | 
|  | 10 |  |  |  |  | 35201 |  | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | sub recursive_update { | 
| 49 | 215 |  |  | 215 |  | 1623958 | my %params = @_; | 
| 50 |  |  |  |  |  |  | my ( $self, $updates, $fixed_fields, $object, $resolved, $if_not_submitted, | 
| 51 |  |  |  |  |  |  | $unknown_params_ok, $m2m_force_set_rel ) | 
| 52 |  |  |  |  |  |  | = @params{ | 
| 53 | 215 |  |  |  |  | 1104 | qw/resultset updates fixed_fields object resolved if_not_submitted unknown_params_ok m2m_force_set_rel/ | 
| 54 |  |  |  |  |  |  | }; | 
| 55 | 215 |  | 100 |  |  | 1059 | $resolved ||= {}; | 
| 56 | 215 |  |  |  |  | 1594 | $ENV{DBIC_NULLABLE_KEY_NOWARN} = 1; | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 215 |  |  |  |  | 767 | my $source = $self->result_source; | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 215 | 50 |  |  |  | 707 | croak "first parameter needs to be defined" | 
| 61 |  |  |  |  |  |  | unless defined $updates; | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 215 | 50 |  |  |  | 749 | croak "first parameter needs to be a hashref" | 
| 64 |  |  |  |  |  |  | unless ref($updates) eq 'HASH'; | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 215 | 50 | 66 |  |  | 789 | croak 'fixed fields needs to be an arrayref' | 
| 67 |  |  |  |  |  |  | if defined $fixed_fields && ref $fixed_fields ne 'ARRAY'; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 215 |  |  |  |  | 390 | DEBUG and warn "recursive_update: " . $source->name . "\n"; | 
| 70 | 215 | 100 |  |  |  | 610 | DEBUG and warn "object passed, skipping find" . | 
| 71 |  |  |  |  |  |  | (defined $object->id | 
| 72 |  |  |  |  |  |  | ? " (id " . $object->id . ")\n" | 
| 73 |  |  |  |  |  |  | : "\n") | 
| 74 |  |  |  |  |  |  | if defined $object; | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | # always warn about additional parameters if storage debugging is enabled | 
| 77 | 215 | 100 |  |  |  | 882 | $unknown_params_ok = 0 | 
| 78 |  |  |  |  |  |  | if $source->storage->debug; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 215 | 50 | 33 |  |  | 11730 | if ( blessed($updates) && $updates->isa('DBIx::Class::Row') ) { | 
| 81 | 0 |  |  |  |  | 0 | return $updates; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 215 |  |  |  |  | 787 | my @pks = $source->primary_columns; | 
| 85 | 215 |  |  |  |  | 1844 | my %pk_kvs; | 
| 86 | 215 |  |  |  |  | 576 | for my $colname (@pks) { | 
| 87 | 276 | 100 | 66 |  |  | 1188 | if (exists $updates->{$colname} && defined $updates->{$colname}) { | 
| 88 | 138 |  |  |  |  | 466 | $pk_kvs{$colname} = $updates->{$colname}; | 
| 89 | 138 |  |  |  |  | 362 | next; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  | $pk_kvs{$colname} = $resolved->{$colname} | 
| 92 | 138 | 100 | 66 |  |  | 736 | if exists $resolved->{$colname} && defined $resolved->{$colname}; | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  | # support the special case where a method on the related row | 
| 95 |  |  |  |  |  |  | # populates one or more primary key columns and we don't have | 
| 96 |  |  |  |  |  |  | # all primary key values already | 
| 97 |  |  |  |  |  |  | # see DBSchema::Result::DVD relationship keysbymethod | 
| 98 | 215 |  |  |  |  | 429 | DEBUG and warn "pk columns so far: " . join (', ', | 
| 99 |  |  |  |  |  |  | sort keys %pk_kvs) . "\n"; | 
| 100 |  |  |  |  |  |  | my @non_pk_columns = grep { | 
| 101 | 215 |  |  |  |  | 1081 | my $colname = $_; | 
|  | 405 |  |  |  |  | 869 |  | 
| 102 | 405 |  |  | 266 |  | 2542 | none { $colname eq $_ } keys %pk_kvs | 
|  | 266 |  |  |  |  | 1337 |  | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | sort keys %$updates; | 
| 105 | 215 |  |  |  |  | 491 | DEBUG and warn "non-pk columns: " . join (', ', | 
| 106 |  |  |  |  |  |  | @non_pk_columns) . "\n"; | 
| 107 | 215 | 100 | 66 |  |  | 1035 | if ( scalar keys %pk_kvs != scalar @pks && @non_pk_columns) { | 
| 108 | 78 |  |  |  |  | 208 | DEBUG and warn "not all primary keys available, trying " . | 
| 109 |  |  |  |  |  |  | "object creation\n"; | 
| 110 |  |  |  |  |  |  | # new_result throws exception if non column values are passed | 
| 111 |  |  |  |  |  |  | # because we want to also support e.g. a BUILDARGS method that | 
| 112 |  |  |  |  |  |  | # populates primary key columns from an additional value | 
| 113 |  |  |  |  |  |  | # filter out all relationships | 
| 114 |  |  |  |  |  |  | my @non_rel_columns = grep { | 
| 115 | 78 |  | 100 |  |  | 267 | !is_m2m( $self, $_ ) | 
|  | 183 |  |  |  |  | 2890 |  | 
| 116 |  |  |  |  |  |  | && !$source->has_relationship($_) | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | sort keys %$updates; | 
| 119 |  |  |  |  |  |  | my %non_rel_updates = map { | 
| 120 | 78 |  |  |  |  | 2011 | $_ => $updates->{$_} | 
|  | 119 |  |  |  |  | 425 |  | 
| 121 |  |  |  |  |  |  | } @non_rel_columns; | 
| 122 |  |  |  |  |  |  | # transform columns specified by their accessor name | 
| 123 | 78 |  |  |  |  | 265 | my %columns_by_accessor = _get_columns_by_accessor($self); | 
| 124 | 78 |  |  |  |  | 455 | for my $accessor_name (sort keys %columns_by_accessor) { | 
| 125 | 353 |  |  |  |  | 679 | my $colname = $columns_by_accessor{$accessor_name}->{name}; | 
| 126 | 353 | 100 | 100 |  |  | 910 | if ($accessor_name ne $colname | 
| 127 |  |  |  |  |  |  | && exists $non_rel_updates{$accessor_name}) { | 
| 128 | 12 |  |  |  |  | 20 | DEBUG and warn "renaming column accessor " . | 
| 129 |  |  |  |  |  |  | "'$accessor_name' to column name '$colname'\n"; | 
| 130 |  |  |  |  |  |  | $non_rel_updates{$colname} = delete | 
| 131 | 12 |  |  |  |  | 40 | $non_rel_updates{$accessor_name}; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 | 78 |  |  |  |  | 157 | DEBUG and warn "using all non-rel updates for object " . | 
| 135 |  |  |  |  |  |  | "construction: " . Dumper(\%non_rel_updates); | 
| 136 |  |  |  |  |  |  | # the object creation might fail because of non-column and | 
| 137 |  |  |  |  |  |  | # non-constructor handled parameters which shouldn't break RU | 
| 138 |  |  |  |  |  |  | try { | 
| 139 | 78 |  |  | 78 |  | 5925 | my $row = $self->new_result(\%non_rel_updates); | 
| 140 | 75 |  |  |  |  | 10157 | for my $colname (@pks) { | 
| 141 |  |  |  |  |  |  | next | 
| 142 | 81 | 100 |  |  |  | 347 | if exists $pk_kvs{$colname}; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 77 | 100 | 66 |  |  | 2444 | if ($row->can($colname) | 
| 145 |  |  |  |  |  |  | && defined $row->$colname) { | 
| 146 | 6 |  |  |  |  | 125 | DEBUG and warn "missing pk column $colname exists " . | 
| 147 |  |  |  |  |  |  | "and defined on object\n"; | 
| 148 | 6 |  |  |  |  | 131 | $pk_kvs{$colname} = $row->$colname; | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  | else { | 
| 151 | 71 |  |  |  |  | 2346 | DEBUG and warn "missing pk column $colname doesn't " | 
| 152 |  |  |  |  |  |  | . "exist or isn't defined on object, aborting\n"; | 
| 153 | 71 |  |  |  |  | 344 | last; | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | catch { | 
| 158 | 3 |  |  | 3 |  | 5714 | DEBUG and warn "object construction failed, ignoring: | 
| 159 |  |  |  |  |  |  | $_\n"; | 
| 160 | 78 |  |  |  |  | 837 | }; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | # check if row can be found in resultset cache | 
| 164 | 215 | 100 | 100 |  |  | 3854 | if ( !defined $object && scalar keys %pk_kvs == scalar @pks ) { | 
| 165 | 80 |  |  |  |  | 362 | my $cached_rows = $self->get_cache; | 
| 166 | 80 | 100 |  |  |  | 491 | if (defined $cached_rows) { | 
| 167 | 4 |  |  |  |  | 8 | DEBUG and warn "find in cache\n"; | 
| 168 | 4 |  |  |  |  | 24 | $object = _get_matching_row(\%pk_kvs, $cached_rows) | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 215 |  |  |  |  | 944 | $updates = { %$updates, %$resolved }; | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 215 |  |  |  |  | 750 | my %fixed_fields = map { $_ => 1 } @$fixed_fields; | 
|  | 2 |  |  |  |  | 8 |  | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | # add the resolved columns to the updates hashref | 
| 177 | 215 |  |  |  |  | 729 | my %all_pks = ( %pk_kvs, %fixed_fields ); | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 215 | 100 | 100 |  |  | 953 | if ( !defined $object && scalar keys %all_pks == scalar @pks) { | 
| 180 | 76 |  |  |  |  | 162 | DEBUG and warn "find by pk\n"; | 
| 181 | 76 |  |  |  |  | 471 | $object = $self->find( \%all_pks, { key => 'primary' } ); | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 215 | 100 |  |  |  | 246301 | unless (defined $object) { | 
| 185 | 62 |  |  |  |  | 116 | DEBUG and warn "create new row\n"; | 
| 186 | 62 |  |  |  |  | 267 | $object = $self->new_result( {} ); | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | # direct column accessors | 
| 190 | 215 |  |  |  |  | 6588 | my %columns; | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # relations that that should be done before the row is inserted into the | 
| 193 |  |  |  |  |  |  | # database like belongs_to | 
| 194 |  |  |  |  |  |  | my %pre_updates; | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | # relations that that should be done after the row is inserted into the | 
| 197 |  |  |  |  |  |  | # database like has_many, might_have and has_one | 
| 198 | 215 |  |  |  |  | 0 | my %post_updates; | 
| 199 | 215 |  |  |  |  | 0 | my %other_methods; | 
| 200 | 215 |  |  |  |  | 0 | my %m2m_accessors; | 
| 201 | 215 |  |  |  |  | 652 | my %columns_by_accessor = _get_columns_by_accessor($self); | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | # this section determines to what each key/value pair maps to, | 
| 204 |  |  |  |  |  |  | # column or relationship | 
| 205 | 215 |  |  |  |  | 1162 | for my $name ( sort keys %$updates ) { | 
| 206 |  |  |  |  |  |  | DEBUG and warn "updating $name to " | 
| 207 | 496 |  |  |  |  | 1451 | . ($updates->{$name} // '[undef]') . "\n"; | 
| 208 |  |  |  |  |  |  | # columns | 
| 209 | 496 | 100 | 100 |  |  | 1897 | if ( exists $columns_by_accessor{$name} && | 
|  |  |  | 100 |  |  |  |  | 
| 210 |  |  |  |  |  |  | !( $source->has_relationship($name) && ref( $updates->{$name} ) ) ) { | 
| 211 | 355 |  |  |  |  | 2895 | $columns{$name} = $updates->{$name}; | 
| 212 | 355 |  |  |  |  | 844 | next; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # relationships | 
| 216 | 141 | 100 |  |  |  | 781 | if ( $source->has_relationship($name) ) { | 
| 217 | 112 | 100 |  |  |  | 699 | if ( _master_relation_cond( $self, $name ) ) { | 
| 218 | 63 |  |  |  |  | 188 | $pre_updates{$name} = $updates->{$name}; | 
| 219 | 63 |  |  |  |  | 194 | next; | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | else { | 
| 222 | 49 |  |  |  |  | 147 | $post_updates{$name} = $updates->{$name}; | 
| 223 | 49 |  |  |  |  | 140 | next; | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | # many-to-many helper accessors | 
| 228 | 29 | 100 |  |  |  | 292 | if ( is_m2m( $self, $name ) ) { | 
| 229 | 24 |  |  |  |  | 648 | DEBUG and warn "is m2m\n"; | 
| 230 |  |  |  |  |  |  | # Transform m2m data into recursive has_many data | 
| 231 |  |  |  |  |  |  | # if IntrospectableM2M is in use. | 
| 232 |  |  |  |  |  |  | # | 
| 233 |  |  |  |  |  |  | # This removes the overhead related to deleting and | 
| 234 |  |  |  |  |  |  | # re-adding all relationships. | 
| 235 | 24 | 100 | 100 |  |  | 594 | if ( !$m2m_force_set_rel && $source->result_class->can('_m2m_metadata') ) { | 
| 236 | 17 |  |  |  |  | 872 | my $meta        = $source->result_class->_m2m_metadata->{$name}; | 
| 237 | 17 |  |  |  |  | 1022 | my $bridge_rel  = $meta->{relation}; | 
| 238 | 17 |  |  |  |  | 58 | my $foreign_rel = $meta->{foreign_relation}; | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | $post_updates{$bridge_rel} = [ | 
| 241 |  |  |  |  |  |  | map { | 
| 242 | 39 |  |  |  |  | 168 | { $foreign_rel => $_ } | 
| 243 | 17 |  |  |  |  | 47 | } @{ $updates->{$name} } | 
|  | 17 |  |  |  |  | 63 |  | 
| 244 |  |  |  |  |  |  | ]; | 
| 245 |  |  |  |  |  |  | DEBUG and warn "m2m '$name' transformed to:\n$bridge_rel => " . | 
| 246 | 17 |  |  |  |  | 46 | Dumper($post_updates{$bridge_rel}) . "\n"; | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  | # Fall back to set_$rel if IntrospectableM2M | 
| 249 |  |  |  |  |  |  | # is not available. (removing and re-adding all relationships) | 
| 250 |  |  |  |  |  |  | else { | 
| 251 | 7 |  |  |  |  | 161 | $m2m_accessors{$name} = $updates->{$name}; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 | 24 |  |  |  |  | 79 | next; | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | # accessors | 
| 258 | 5 | 100 | 66 |  |  | 112 | if ( $object->can($name) && not $source->has_relationship($name) ) { | 
| 259 | 2 |  |  |  |  | 21 | $other_methods{$name} = $updates->{$name}; | 
| 260 | 2 |  |  |  |  | 7 | next; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | # unknown | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | # don't throw a warning instead of an exception to give users | 
| 266 |  |  |  |  |  |  | # time to adapt to the new API | 
| 267 |  |  |  |  |  |  | carp( | 
| 268 | 3 | 100 |  |  |  | 24 | "No such column, relationship, many-to-many helper accessor or " . | 
| 269 |  |  |  |  |  |  | "generic accessor '$name' on '" . $source->name . "'" | 
| 270 |  |  |  |  |  |  | ) unless $unknown_params_ok; | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | # first update columns and other accessors | 
| 275 |  |  |  |  |  |  | # so that later related records can be found | 
| 276 | 215 |  |  |  |  | 832 | for my $name ( sort keys %columns ) { | 
| 277 | 343 |  |  |  |  | 26247 | $object->$name( $columns{$name} ); | 
| 278 |  |  |  |  |  |  | } | 
| 279 | 209 |  |  |  |  | 23395 | for my $name ( sort keys %other_methods ) { | 
| 280 | 2 |  |  |  |  | 8 | $object->$name( $other_methods{$name} ); | 
| 281 |  |  |  |  |  |  | } | 
| 282 | 209 |  |  |  |  | 998 | for my $name ( sort keys %pre_updates ) { | 
| 283 | 61 |  |  |  |  | 477 | _update_relation( $self, $name, $pre_updates{$name}, $object, $if_not_submitted, 0 ); | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | # $self->_delete_empty_auto_increment($object); | 
| 287 |  |  |  |  |  |  | # don't allow insert to recurse to related objects | 
| 288 |  |  |  |  |  |  | # do the recursion ourselves | 
| 289 |  |  |  |  |  |  | # $object->{_rel_in_storage} = 1; | 
| 290 |  |  |  |  |  |  | # Update if %other_methods because of possible custom update method | 
| 291 | 205 |  |  |  |  | 1833 | my $in_storage = $object->in_storage; | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | # preserve related resultsets as DBIx::Class::Row->update clears them | 
| 294 |  |  |  |  |  |  | # yes, this directly accesses a row attribute, but no API exists and in | 
| 295 |  |  |  |  |  |  | # the hope to get the recursive_update feature into core DBIx::Class this | 
| 296 |  |  |  |  |  |  | # is the easiest solution | 
| 297 | 205 |  |  |  |  | 541 | my $related_resultsets = $object->{related_resultsets}; | 
| 298 |  |  |  |  |  |  |  | 
| 299 | 205 |  |  |  |  | 351 | DEBUG and warn "before update_or_insert\n"; | 
| 300 | 205 | 100 | 66 |  |  | 1359 | $object->update_or_insert if ( $object->is_changed || keys %other_methods ); | 
| 301 | 205 |  |  |  |  | 1471348 | DEBUG and warn "after update_or_insert\n"; | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | # restore related resultsets | 
| 304 | 205 |  |  |  |  | 669 | $object->{related_resultsets} = $related_resultsets; | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | # updating many_to_many | 
| 307 | 205 |  |  |  |  | 800 | for my $name ( sort keys %m2m_accessors ) { | 
| 308 | 7 |  |  |  |  | 18 | DEBUG and warn "updating m2m $name\n"; | 
| 309 | 7 |  |  |  |  | 26 | my $value = $m2m_accessors{$name}; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # TODO: only first pk col is used | 
| 312 | 7 |  |  |  |  | 33 | my ($pk) = _get_pk_for_related( $self, $name ); | 
| 313 | 7 |  |  |  |  | 57 | my @rows; | 
| 314 | 7 |  |  |  |  | 32 | my $rel_source = $object->$name->result_source; | 
| 315 | 7 |  |  |  |  | 22408 | my @updates; | 
| 316 | 7 | 50 | 33 |  |  | 172 | if ( defined $value && ref $value eq 'ARRAY' ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 317 | 7 |  |  |  |  | 17 | @updates = @{$value}; | 
|  | 7 |  |  |  |  | 25 |  | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  | elsif ( defined $value && !ref $value ) { | 
| 320 | 0 |  |  |  |  | 0 | @updates = ($value); | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  | elsif ( defined $value ) { | 
| 323 | 0 |  |  |  |  | 0 | carp "value of many-to-many rel '$name' must be an arrayref or scalar: $value"; | 
| 324 |  |  |  |  |  |  | } | 
| 325 | 7 |  |  |  |  | 20 | for my $elem (@updates) { | 
| 326 | 16 | 50 | 33 |  |  | 19787 | if ( blessed($elem) && $elem->isa('DBIx::Class::Row') ) { | 
|  |  | 100 |  |  |  |  |  | 
| 327 | 0 |  |  |  |  | 0 | push @rows, $elem; | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  | elsif ( ref $elem eq 'HASH' ) { | 
| 330 | 5 |  |  |  |  | 25 | push @rows, | 
| 331 |  |  |  |  |  |  | recursive_update( | 
| 332 |  |  |  |  |  |  | resultset => $rel_source->resultset, | 
| 333 |  |  |  |  |  |  | updates   => $elem | 
| 334 |  |  |  |  |  |  | ); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | else { | 
| 337 | 11 |  |  |  |  | 44 | push @rows, $rel_source->resultset->find( { $pk => $elem } ); | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  | } | 
| 340 | 7 |  |  |  |  | 15764 | my $set_meth = 'set_' . $name; | 
| 341 | 7 |  |  |  |  | 61 | $object->$set_meth( \@rows ); | 
| 342 |  |  |  |  |  |  | } | 
| 343 | 205 |  |  |  |  | 461802 | for my $name ( sort keys %post_updates ) { | 
| 344 | 57 |  |  |  |  | 435 | _update_relation( $self, $name, $post_updates{$name}, $object, $if_not_submitted, $in_storage ); | 
| 345 |  |  |  |  |  |  | } | 
| 346 | 200 |  |  |  |  | 2974 | delete $ENV{DBIC_NULLABLE_KEY_NOWARN}; | 
| 347 | 200 |  |  |  |  | 2342 | return $object; | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | # returns DBIx::Class::ResultSource::column_info as a hash indexed by column accessor || name | 
| 351 |  |  |  |  |  |  | sub _get_columns_by_accessor { | 
| 352 | 316 |  |  | 316 |  | 770 | my $self   = shift; | 
| 353 | 316 |  |  |  |  | 860 | my $source = $self->result_source; | 
| 354 | 316 |  |  |  |  | 648 | my %columns; | 
| 355 | 316 |  |  |  |  | 1139 | for my $name ( $source->columns ) { | 
| 356 | 1295 |  |  |  |  | 5880 | my $info = $source->column_info($name); | 
| 357 | 1295 |  |  |  |  | 12634 | $info->{name} = $name; | 
| 358 | 1295 |  | 66 |  |  | 4948 | $columns{ $info->{accessor} || $name } = $info; | 
| 359 |  |  |  |  |  |  | } | 
| 360 | 316 |  |  |  |  | 1826 | return %columns; | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub _get_matching_row { | 
| 364 | 67 |  |  | 67 |  | 212 | my ($kvs, $rows) = @_; | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | return | 
| 367 | 67 | 50 |  |  |  | 217 | unless defined $rows; | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 67 | 50 |  |  |  | 283 | croak 'key/value need to be a hashref' | 
| 370 |  |  |  |  |  |  | unless ref $kvs eq 'HASH'; | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 67 | 50 |  |  |  | 221 | croak 'key/value needs to have at least one pair' | 
| 373 |  |  |  |  |  |  | if keys %$kvs == 0; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 67 | 50 |  |  |  | 252 | croak 'rows need to be an arrayref' | 
| 376 |  |  |  |  |  |  | unless ref $rows eq 'ARRAY'; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 67 | 50 |  |  |  | 230 | unless ($rows) { | 
| 379 | 0 |  |  |  |  | 0 | DEBUG and warn "skipping because no rows passed\n"; | 
| 380 | 0 |  |  |  |  | 0 | return; | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 | 67 |  |  |  |  | 168 | my $matching_row; | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | my @matching_rows; | 
| 386 | 67 |  |  |  |  | 193 | for my $row (@$rows) { | 
| 387 |  |  |  |  |  |  | push @matching_rows, $row | 
| 388 | 234 |  |  | 234 |  | 1597 | if all { $kvs->{$_} eq $row->get_column($_) } | 
| 389 | 134 | 100 |  |  |  | 2030 | grep { !ref $kvs->{$_} } | 
|  | 237 |  |  |  |  | 840 |  | 
| 390 |  |  |  |  |  |  | sort keys %$kvs; | 
| 391 |  |  |  |  |  |  | } | 
| 392 | 67 | 50 |  |  |  | 778 | DEBUG and warn "multiple matching rows: " . scalar @matching_rows . "\n" | 
| 393 |  |  |  |  |  |  | if @matching_rows > 1; | 
| 394 | 67 | 100 |  |  |  | 211 | $matching_row = $matching_rows[0] | 
| 395 |  |  |  |  |  |  | if scalar @matching_rows == 1; | 
| 396 |  |  |  |  |  |  | DEBUG and warn "matching row found for: " . Dumper($kvs) . " in " . | 
| 397 | 67 | 100 |  |  |  | 193 | Dumper([map { { $_->get_columns } } @$rows]) . "\n" | 
| 398 |  |  |  |  |  |  | if defined $matching_row; | 
| 399 |  |  |  |  |  |  | DEBUG and warn "matching row not found for: " . Dumper($kvs) . " in " . | 
| 400 | 67 | 100 |  |  |  | 188 | Dumper([map { { $_->get_columns } } @$rows]) . "\n" | 
| 401 |  |  |  |  |  |  | unless defined $matching_row; | 
| 402 |  |  |  |  |  |  |  | 
| 403 | 67 |  |  |  |  | 513 | return $matching_row; | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | # Arguments: $rs, $name, $updates, $row, $if_not_submitted, $row_existed | 
| 407 |  |  |  |  |  |  | sub _update_relation { | 
| 408 | 118 |  |  | 118 |  | 408 | my ( $self, $name, $updates, $object, $if_not_submitted, $row_existed ) = @_; | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | # this should never happen because we're checking the paramters passed to | 
| 411 |  |  |  |  |  |  | # recursive_update, but just to be sure... | 
| 412 | 118 | 50 |  |  |  | 3585 | $object->throw_exception("No such relationship '$name'") | 
| 413 |  |  |  |  |  |  | unless $object->has_relationship($name); | 
| 414 |  |  |  |  |  |  |  | 
| 415 | 118 |  |  |  |  | 14489 | DEBUG and warn "_update_relation: $name\n"; | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 118 |  |  |  |  | 519 | my $info = $object->result_source->relationship_info($name); | 
| 418 | 118 |  |  |  |  | 1265 | my $attrs = $info->{attrs}; | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | # get a related resultset without a condition | 
| 421 | 118 |  |  |  |  | 607 | my $related_source = $self->related_resultset($name)->result_source; | 
| 422 | 118 |  |  |  |  | 136383 | my $related_resultset = $related_source->resultset; | 
| 423 | 118 | 50 |  |  |  | 39973 | $self->throw_exception("result_source must support _resolve_condition") | 
| 424 |  |  |  |  |  |  | unless $self->result_source->can('_resolve_condition'); | 
| 425 | 118 |  |  |  |  | 608 | my $resolved = $self->result_source->_resolve_condition( $info->{cond}, $name, $object, $name ); | 
| 426 |  |  |  |  |  |  |  | 
| 427 | 118 | 100 | 66 |  |  | 37997 | $resolved = {} | 
| 428 |  |  |  |  |  |  | if defined $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION && | 
| 429 |  |  |  |  |  |  | $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION == $resolved; | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | # This is a hack. I'm not sure that this will handle most | 
| 432 |  |  |  |  |  |  | # custom code conditions yet. This needs tests. | 
| 433 | 118 |  |  |  |  | 315 | my @rel_cols; | 
| 434 | 118 | 50 |  |  |  | 507 | if ( ref $info->{cond} eq 'CODE' ) { | 
| 435 | 0 |  |  |  |  | 0 | my $new_resolved; | 
| 436 |  |  |  |  |  |  | # remove 'me.' from keys in returned hashref | 
| 437 | 0 |  |  |  |  | 0 | while ( my ( $key, $value ) = each  %$resolved ) { | 
| 438 | 0 |  |  |  |  | 0 | $key =~ s/^me\.//; | 
| 439 | 0 |  |  |  |  | 0 | $new_resolved->{$key} = $value; | 
| 440 | 0 |  |  |  |  | 0 | push @rel_cols, $key; | 
| 441 |  |  |  |  |  |  | } | 
| 442 | 0 |  |  |  |  | 0 | $resolved = $new_resolved; | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  | else { | 
| 445 | 118 |  |  |  |  | 293 | @rel_cols = sort keys %{ $info->{cond} }; | 
|  | 118 |  |  |  |  | 510 |  | 
| 446 | 118 |  |  |  |  | 378 | map { s/^foreign\.// } @rel_cols; | 
|  | 118 |  |  |  |  | 655 |  | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | # find out if all related columns are nullable | 
| 450 | 118 |  |  |  |  | 287 | my $all_fks_nullable = 1; | 
| 451 | 118 |  |  |  |  | 316 | for my $rel_col (@rel_cols) { | 
| 452 |  |  |  |  |  |  | $all_fks_nullable = 0 | 
| 453 | 118 | 100 |  |  |  | 645 | unless $related_resultset->result_source->column_info($rel_col)->{is_nullable}; | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  |  | 
| 456 | 118 | 100 |  |  |  | 2005 | $if_not_submitted = $all_fks_nullable ? 'set_to_null' : 'delete' | 
|  |  | 100 |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | unless defined $if_not_submitted; | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | # the only valid datatype for a has_many rels is an arrayref | 
| 460 | 118 | 100 | 66 |  |  | 731 | if ( $attrs->{accessor} eq 'multi' ) { | 
|  |  | 50 |  |  |  |  |  | 
| 461 | 49 |  |  |  |  | 109 | DEBUG and warn "has_many: $name\n"; | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | # handle undef like empty arrayref | 
| 464 | 49 | 100 |  |  |  | 187 | $updates = [] | 
| 465 |  |  |  |  |  |  | unless defined $updates; | 
| 466 | 49 | 50 |  |  |  | 188 | $self->throw_exception("data for has_many relationship '$name' must be an arrayref") | 
| 467 |  |  |  |  |  |  | unless ref $updates eq 'ARRAY'; | 
| 468 |  |  |  |  |  |  |  | 
| 469 | 49 |  |  |  |  | 221 | my @updated_objs; | 
| 470 |  |  |  |  |  |  | my @related_rows; | 
| 471 |  |  |  |  |  |  | # newly created rows can't have related rows | 
| 472 | 49 | 100 |  |  |  | 179 | if ($row_existed) { | 
| 473 | 28 |  |  |  |  | 680 | @related_rows = $object->$name; | 
| 474 | 28 |  |  |  |  | 100028 | DEBUG and warn "got related rows: " . scalar @related_rows . "\n"; | 
| 475 |  |  |  |  |  |  | } | 
| 476 | 49 |  |  |  |  | 197 | my $related_result_source = $related_resultset->result_source; | 
| 477 | 49 |  |  |  |  | 202 | my @pks = $related_result_source->primary_columns; | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 49 |  |  |  |  | 500 | for my $sub_updates ( @{$updates} ) { | 
|  | 49 |  |  |  |  | 153 |  | 
| 480 | 85 |  |  |  |  | 157 | DEBUG and warn "updating related row: " . Dumper($sub_updates) | 
| 481 |  |  |  |  |  |  | . "\n"; | 
| 482 | 85 |  |  |  |  | 242 | my %pk_kvs; | 
| 483 |  |  |  |  |  |  | # detect the special case where the primary key of a currently not | 
| 484 |  |  |  |  |  |  | # related row is passed in the updates hash | 
| 485 |  |  |  |  |  |  | # let the resolved column values fill any missing primary key | 
| 486 |  |  |  |  |  |  | # columns but not overwrite them | 
| 487 | 85 |  |  |  |  | 229 | for my $colname (@pks) { | 
| 488 | 142 | 100 | 66 |  |  | 675 | if (exists $sub_updates->{$colname} | 
| 489 |  |  |  |  |  |  | && defined $sub_updates->{$colname}) { | 
| 490 |  |  |  |  |  |  | # $sub_updates->{$colname} might be a hashref if a | 
| 491 |  |  |  |  |  |  | # relationship is named the same as a foreign key column | 
| 492 | 66 | 100 |  |  |  | 306 | if (ref $sub_updates->{$colname} eq 'HASH') { | 
| 493 | 29 | 50 |  |  |  | 140 | if ($related_source->has_relationship($colname)) { | 
| 494 | 29 |  |  |  |  | 274 | my $rel_info = $related_source | 
| 495 |  |  |  |  |  |  | ->relationship_info($colname); | 
| 496 | 29 |  |  |  |  | 153 | my @rel_cols = sort keys %{ $rel_info->{cond} }; | 
|  | 29 |  |  |  |  | 160 |  | 
| 497 | 29 |  |  |  |  | 107 | map { s/^foreign\.// } @rel_cols; | 
|  | 29 |  |  |  |  | 217 |  | 
| 498 | 29 | 50 |  |  |  | 124 | $self->throw_exception("passing a hashref for " . | 
| 499 |  |  |  |  |  |  | "a multi-column relationship named the " . | 
| 500 |  |  |  |  |  |  | "same as a column ('$colname') is not " . | 
| 501 |  |  |  |  |  |  | "implemented") | 
| 502 |  |  |  |  |  |  | if scalar @rel_cols != 1; | 
| 503 | 29 |  |  |  |  | 74 | DEBUG and warn "using '$rel_cols[0]' in hashref " . | 
| 504 |  |  |  |  |  |  | "for primary key column '$colname'\n"; | 
| 505 |  |  |  |  |  |  | $pk_kvs{$colname} = $sub_updates->{$colname} | 
| 506 | 29 |  |  |  |  | 132 | ->{$rel_cols[0]}; | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  | else { | 
| 509 | 0 |  |  |  |  | 0 | $self->throw_exception( | 
| 510 |  |  |  |  |  |  | "data for $colname is a hashref but no " . | 
| 511 |  |  |  |  |  |  | "relationship with that name exists"); | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  | else { | 
| 515 | 37 |  |  |  |  | 139 | $pk_kvs{$colname} = $sub_updates->{$colname}; | 
| 516 |  |  |  |  |  |  | } | 
| 517 | 66 |  |  |  |  | 200 | next; | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  | $pk_kvs{$colname} = $resolved->{$colname} | 
| 520 |  |  |  |  |  |  | if exists $resolved->{$colname} | 
| 521 | 76 | 100 | 66 |  |  | 498 | && defined $resolved->{$colname}; | 
| 522 |  |  |  |  |  |  | } | 
| 523 | 85 |  |  |  |  | 178 | my $related_object; | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | # support the special case where a method on the related row | 
| 526 |  |  |  |  |  |  | # populates one or more primary key columns and we don't have | 
| 527 |  |  |  |  |  |  | # all primary key values already | 
| 528 |  |  |  |  |  |  | # see DBSchema::Result::DVD relationship keysbymethod | 
| 529 | 85 |  |  |  |  | 163 | DEBUG and warn "pk columns so far: " . join (', ', | 
| 530 |  |  |  |  |  |  | sort keys %pk_kvs) . "\n"; | 
| 531 |  |  |  |  |  |  | my @non_pk_columns = grep { | 
| 532 | 85 |  |  |  |  | 422 | my $colname = $_; | 
|  | 112 |  |  |  |  | 254 |  | 
| 533 | 112 |  |  | 100 |  | 866 | none { $colname eq $_ } keys %pk_kvs | 
|  | 100 |  |  |  |  | 561 |  | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  | sort keys %$sub_updates; | 
| 536 | 85 |  |  |  |  | 202 | DEBUG and warn "non-pk columns: " . join (', ', | 
| 537 |  |  |  |  |  |  | @non_pk_columns) . "\n"; | 
| 538 | 85 | 100 | 66 |  |  | 452 | if ( scalar keys %pk_kvs != scalar @pks && @non_pk_columns) { | 
| 539 | 23 |  |  |  |  | 47 | DEBUG and warn "not all primary keys available, trying " . | 
| 540 |  |  |  |  |  |  | "object creation\n"; | 
| 541 |  |  |  |  |  |  | # new_result throws exception if non column values are passed | 
| 542 |  |  |  |  |  |  | # because we want to also support e.g. a BUILDARGS method that | 
| 543 |  |  |  |  |  |  | # populates primary key columns from an additional value | 
| 544 |  |  |  |  |  |  | # filter out all relationships | 
| 545 |  |  |  |  |  |  | my @non_rel_columns = grep { | 
| 546 | 23 |  | 100 |  |  | 77 | !is_m2m( $related_resultset, $_ ) | 
|  | 43 |  |  |  |  | 633 |  | 
| 547 |  |  |  |  |  |  | && !$related_result_source->has_relationship($_) | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  | sort keys %$sub_updates; | 
| 550 |  |  |  |  |  |  | my %non_rel_updates = map { | 
| 551 | 23 |  |  |  |  | 602 | $_ => $sub_updates->{$_} | 
|  | 35 |  |  |  |  | 132 |  | 
| 552 |  |  |  |  |  |  | } @non_rel_columns; | 
| 553 |  |  |  |  |  |  | # transform columns specified by their accessor name | 
| 554 | 23 |  |  |  |  | 83 | my %columns_by_accessor = _get_columns_by_accessor($related_resultset); | 
| 555 | 23 |  |  |  |  | 162 | for my $accessor_name (sort keys %columns_by_accessor) { | 
| 556 | 111 |  |  |  |  | 253 | my $colname = $columns_by_accessor{$accessor_name}->{name}; | 
| 557 | 111 | 100 | 100 |  |  | 336 | if ($accessor_name ne $colname | 
| 558 |  |  |  |  |  |  | && exists $non_rel_updates{$accessor_name}) { | 
| 559 | 10 |  |  |  |  | 18 | DEBUG and warn "renaming column accessor " . | 
| 560 |  |  |  |  |  |  | "'$accessor_name' to column name '$colname'\n"; | 
| 561 |  |  |  |  |  |  | $non_rel_updates{$colname} = delete | 
| 562 | 10 |  |  |  |  | 29 | $non_rel_updates{$accessor_name}; | 
| 563 |  |  |  |  |  |  | } | 
| 564 |  |  |  |  |  |  | } | 
| 565 | 23 |  |  |  |  | 51 | DEBUG and warn "using all non-rel updates for object " . | 
| 566 |  |  |  |  |  |  | "construction: " . Dumper(\%non_rel_updates); | 
| 567 |  |  |  |  |  |  | # the object creation might fail because of non-column and | 
| 568 |  |  |  |  |  |  | # non-constructor handled parameters which shouldn't break RU | 
| 569 |  |  |  |  |  |  | try { | 
| 570 | 23 |  |  | 23 |  | 1881 | my $related_row = $related_resultset | 
| 571 |  |  |  |  |  |  | ->new_result(\%non_rel_updates); | 
| 572 | 23 |  |  |  |  | 3170 | for my $colname (@pks) { | 
| 573 |  |  |  |  |  |  | next | 
| 574 | 27 | 100 |  |  |  | 118 | if exists $pk_kvs{$colname}; | 
| 575 |  |  |  |  |  |  |  | 
| 576 | 24 | 100 | 66 |  |  | 848 | if ($related_row->can($colname) | 
| 577 |  |  |  |  |  |  | && defined $related_row->$colname) { | 
| 578 | 2 |  |  |  |  | 34 | DEBUG and warn "missing pk column $colname exists " . | 
| 579 |  |  |  |  |  |  | "and defined on object\n"; | 
| 580 | 2 |  |  |  |  | 39 | $pk_kvs{$colname} = $related_row->$colname; | 
| 581 |  |  |  |  |  |  | } | 
| 582 |  |  |  |  |  |  | else { | 
| 583 | 22 |  |  |  |  | 784 | DEBUG and warn "missing pk column $colname doesn't " | 
| 584 |  |  |  |  |  |  | . "exist or isn't defined on object, aborting\n"; | 
| 585 | 22 |  |  |  |  | 132 | last; | 
| 586 |  |  |  |  |  |  | } | 
| 587 |  |  |  |  |  |  | } | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  | catch { | 
| 590 | 0 |  |  | 0 |  | 0 | DEBUG and warn "object construction failed, ignoring: | 
| 591 |  |  |  |  |  |  | $_\n"; | 
| 592 | 23 |  |  |  |  | 243 | }; | 
| 593 |  |  |  |  |  |  | } | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | # The only reasons to let recursive_update search for an existing | 
| 596 |  |  |  |  |  |  | # row (= not passing a new result to it) is relinking of existing | 
| 597 |  |  |  |  |  |  | # rows. | 
| 598 |  |  |  |  |  |  | # Relinking is only possible if all primary key column values are | 
| 599 |  |  |  |  |  |  | # known and only required if at least one of the foreign row | 
| 600 |  |  |  |  |  |  | # columns, which are part of the relationship, differ between | 
| 601 |  |  |  |  |  |  | # current and target ones. | 
| 602 |  |  |  |  |  |  | # There are two different cases: | 
| 603 |  |  |  |  |  |  | # The foreign row columns are part of the foreign primary key. | 
| 604 |  |  |  |  |  |  | # An example is the dvdtags relationship of Dvd. | 
| 605 |  |  |  |  |  |  | # Or one or more non primary key form the relationship. | 
| 606 |  |  |  |  |  |  | # An example is the owned_dvds relationship of User. | 
| 607 | 85 |  |  |  |  | 1150 | my $relink = 0; | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 85 | 100 |  |  |  | 392 | if ( scalar keys %pk_kvs == scalar @pks ) { | 
| 610 | 63 |  |  |  |  | 173 | DEBUG and warn "all primary keys available, " . | 
| 611 |  |  |  |  |  |  | "searching for row in currently related rows\n"; | 
| 612 |  |  |  |  |  |  | # the lookup can fail if the primary key of a currently not | 
| 613 |  |  |  |  |  |  | # related row is passed in the updates hash | 
| 614 | 63 |  |  |  |  | 310 | $related_object = _get_matching_row(\%pk_kvs, \@related_rows); | 
| 615 |  |  |  |  |  |  | # %pk_kvs contains the scalar value instead of a hashref | 
| 616 |  |  |  |  |  |  | # when a column and relationship are named the same so | 
| 617 |  |  |  |  |  |  | # overwrite the hashref in $sub_updates with that | 
| 618 |  |  |  |  |  |  | # don't include %$resolved as well as that contains target data | 
| 619 | 63 |  |  |  |  | 396 | my %current_data = (%$sub_updates, %pk_kvs); | 
| 620 | 63 |  |  |  |  | 146 | DEBUG and warn "current data: " . Dumper(\%current_data); | 
| 621 | 63 |  |  |  |  | 131 | DEBUG and warn "target data: " . Dumper($resolved); | 
| 622 |  |  |  |  |  |  |  | 
| 623 | 10 |  |  | 10 |  | 120 | no warnings 'uninitialized'; | 
|  | 10 |  |  |  |  | 28 |  | 
|  | 10 |  |  |  |  | 17745 |  | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  | # If the row can't be found by _get_matching_row it is | 
| 626 |  |  |  |  |  |  | # currently not linked or doesn't even exist. In this case we | 
| 627 |  |  |  |  |  |  | # must execute a sql select to find it. | 
| 628 |  |  |  |  |  |  | $relink = 1 | 
| 629 |  |  |  |  |  |  | if (not defined $related_object) | 
| 630 | 30 |  |  | 30 |  | 211 | && (any { $resolved->{$_} ne $current_data{$_} } | 
| 631 | 63 | 100 | 100 |  |  | 504 | keys %$resolved); | 
| 632 |  |  |  |  |  |  | } | 
| 633 | 85 |  |  |  |  | 308 | DEBUG and warn "relink: $relink\n"; | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | # pass an empty object if no related row found and it's not the | 
| 636 |  |  |  |  |  |  | # special case where the primary key of a currently not related | 
| 637 |  |  |  |  |  |  | # row is passed in the updates hash to prevent the find by pk in | 
| 638 |  |  |  |  |  |  | # recursive_update to happen | 
| 639 | 85 | 100 | 100 |  |  | 422 | if ((not defined $related_object) && (not $relink)) { | 
| 640 | 50 |  |  |  |  | 96 | DEBUG and warn "passing empty row to prevent find by pk\n"; | 
| 641 | 50 |  |  |  |  | 253 | $related_object = $related_resultset->new_result({}); | 
| 642 |  |  |  |  |  |  | } | 
| 643 |  |  |  |  |  |  |  | 
| 644 | 85 |  |  |  |  | 5241 | my $sub_object = recursive_update( | 
| 645 |  |  |  |  |  |  | resultset => $related_resultset, | 
| 646 |  |  |  |  |  |  | updates   => $sub_updates, | 
| 647 |  |  |  |  |  |  | resolved  => $resolved, | 
| 648 |  |  |  |  |  |  | # pass prefetched object if found | 
| 649 |  |  |  |  |  |  | object    => $related_object, | 
| 650 |  |  |  |  |  |  | ); | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 80 |  |  |  |  | 432 | push @updated_objs, $sub_object; | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | # determine if a removal query is required | 
| 656 |  |  |  |  |  |  | my @remove_rows = grep { | 
| 657 | 44 |  |  |  |  | 178 | my $existing_row = $_; | 
|  | 69 |  |  |  |  | 9776 |  | 
| 658 | 94 |  |  | 94 |  | 7750 | none { $existing_row->ID eq $_->ID } @updated_objs | 
| 659 | 69 |  |  |  |  | 486 | } @related_rows; | 
| 660 | 44 |  |  |  |  | 4781 | DEBUG and warn "rows for removal: " .  join(', ', map { $_->ID } | 
| 661 |  |  |  |  |  |  | @remove_rows) . "\n"; | 
| 662 |  |  |  |  |  |  |  | 
| 663 | 44 | 100 |  |  |  | 407 | if (scalar @remove_rows) { | 
| 664 | 17 |  |  |  |  | 515 | my $rs_rel_delist = $object->$name; | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | # foreign table has a single pk column | 
| 667 | 17 | 100 |  |  |  | 4756 | if (scalar @pks == 1) { | 
| 668 | 6 |  |  |  |  | 21 | DEBUG and warn "delete in not_in\n"; | 
| 669 | 6 |  |  |  |  | 50 | $rs_rel_delist = $rs_rel_delist->search_rs( | 
| 670 |  |  |  |  |  |  | { | 
| 671 |  |  |  |  |  |  | $self->current_source_alias . "." . | 
| 672 |  |  |  |  |  |  | $pks[0] => { -not_in => [ map ( $_->id, @updated_objs ) ] } | 
| 673 |  |  |  |  |  |  | } | 
| 674 |  |  |  |  |  |  | ); | 
| 675 |  |  |  |  |  |  | } | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | # foreign table has multiple pk columns | 
| 678 |  |  |  |  |  |  | else { | 
| 679 | 11 |  |  |  |  | 39 | my @cond; | 
| 680 | 11 |  |  |  |  | 44 | for my $obj (@updated_objs) { | 
| 681 | 14 |  |  |  |  | 33 | my %cond_for_obj; | 
| 682 | 14 |  |  |  |  | 39 | for my $col (@pks) { | 
| 683 | 28 |  |  |  |  | 277 | $cond_for_obj{ $self->current_source_alias . ".$col" } = | 
| 684 |  |  |  |  |  |  | $obj->get_column($col); | 
| 685 |  |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | } | 
| 687 | 14 |  |  |  |  | 183 | push @cond, \%cond_for_obj; | 
| 688 |  |  |  |  |  |  | } | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | # only limit resultset if there are related rows left | 
| 691 | 11 | 100 |  |  |  | 46 | if (scalar @cond) { | 
| 692 | 8 |  |  |  |  | 44 | $rs_rel_delist = $rs_rel_delist->search_rs({ -not => [ @cond ] }); | 
| 693 |  |  |  |  |  |  | } | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 17 | 100 |  |  |  | 8442 | if ($if_not_submitted eq 'delete') { | 
|  |  | 50 |  |  |  |  |  | 
| 697 | 15 |  |  |  |  | 145 | $rs_rel_delist->delete; | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  | elsif ($if_not_submitted eq 'set_to_null') { | 
| 700 | 2 |  |  |  |  | 6 | my %update = map {$_ => undef} @rel_cols; | 
|  | 2 |  |  |  |  | 12 |  | 
| 701 | 2 |  |  |  |  | 8 | $rs_rel_delist->update(\%update); | 
| 702 |  |  |  |  |  |  | } | 
| 703 |  |  |  |  |  |  | } | 
| 704 |  |  |  |  |  |  | } | 
| 705 |  |  |  |  |  |  | elsif ( $attrs->{accessor} eq 'single' || | 
| 706 |  |  |  |  |  |  | $attrs->{accessor} eq 'filter' ) { | 
| 707 |  |  |  |  |  |  | DEBUG and warn "has_one, might_have, belongs_to (" . | 
| 708 | 69 |  |  |  |  | 147 | $attrs->{accessor} . "): $name\n"; | 
| 709 |  |  |  |  |  |  |  | 
| 710 | 69 |  |  |  |  | 159 | my $sub_object; | 
| 711 | 69 | 100 |  |  |  | 199 | if ( ref $updates ) { | 
| 712 | 68 |  |  |  |  | 117 | my $existing_row = 0; | 
| 713 | 68 |  |  |  |  | 257 | my @pks = $related_resultset->result_source->primary_columns; | 
| 714 | 68 | 100 |  | 68 |  | 887 | if ( all { exists $updates->{$_} && defined $updates->{$_} } @pks ) { | 
|  | 68 | 100 |  |  |  | 408 |  | 
| 715 | 24 |  |  |  |  | 149 | $existing_row = 1; | 
| 716 |  |  |  |  |  |  | } | 
| 717 | 68 |  |  |  |  | 261 | DEBUG and warn $existing_row ? "existing row\n" : "new row\n"; | 
| 718 |  |  |  |  |  |  | # newly created rows can't have related rows | 
| 719 | 68 |  |  |  |  | 149 | my $related_row; | 
| 720 | 68 | 100 |  |  |  | 192 | if ($row_existed) { | 
| 721 | 2 |  |  |  |  | 39 | $related_row = $object->$name; | 
| 722 | 2 |  |  |  |  | 9612 | DEBUG and warn "got related row\n"; | 
| 723 |  |  |  |  |  |  | } | 
| 724 | 68 | 100 | 66 |  |  | 645 | if ( blessed($updates) && $updates->isa('DBIx::Class::Row') ) { | 
|  |  | 100 | 100 |  |  |  |  | 
| 725 | 10 |  |  |  |  | 42 | $sub_object = $updates; | 
| 726 |  |  |  |  |  |  | } | 
| 727 |  |  |  |  |  |  | elsif ( $attrs->{accessor} eq 'single' && defined $related_row ) | 
| 728 |  |  |  |  |  |  | { | 
| 729 | 1 | 50 |  |  |  | 33 | $sub_object = recursive_update( | 
| 730 |  |  |  |  |  |  | resultset => $related_resultset, | 
| 731 |  |  |  |  |  |  | updates   => $updates, | 
| 732 |  |  |  |  |  |  | $existing_row ? () : (object => $object->$name), | 
| 733 |  |  |  |  |  |  | ); | 
| 734 |  |  |  |  |  |  | } | 
| 735 |  |  |  |  |  |  | else { | 
| 736 | 57 | 100 |  |  |  | 346 | $sub_object = recursive_update( | 
| 737 |  |  |  |  |  |  | resultset => $related_resultset, | 
| 738 |  |  |  |  |  |  | updates   => $updates, | 
| 739 |  |  |  |  |  |  | $existing_row ? () : (resolved => $resolved), | 
| 740 |  |  |  |  |  |  | ); | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  | } | 
| 743 |  |  |  |  |  |  | else { | 
| 744 |  |  |  |  |  |  | $sub_object = $related_resultset->find($updates) | 
| 745 |  |  |  |  |  |  | unless ( | 
| 746 |  |  |  |  |  |  | !$updates && | 
| 747 |  |  |  |  |  |  | ( exists $attrs->{join_type} && | 
| 748 | 1 | 50 | 33 |  |  | 11 | $attrs->{join_type} eq 'LEFT' ) | 
|  |  |  | 33 |  |  |  |  | 
| 749 |  |  |  |  |  |  | ); | 
| 750 |  |  |  |  |  |  | } | 
| 751 | 65 |  | 100 |  |  | 397 | my $join_type = $attrs->{join_type} || ''; | 
| 752 |  |  |  |  |  |  | # unmarked 'LEFT' join for belongs_to | 
| 753 |  |  |  |  |  |  | my $might_belong_to = | 
| 754 |  |  |  |  |  |  | ( $attrs->{accessor} eq 'single' || $attrs->{accessor} eq 'filter' ) && | 
| 755 | 65 |  | 66 |  |  | 545 | $attrs->{is_foreign_key_constraint}; | 
| 756 |  |  |  |  |  |  | # adding check for custom condition that's a coderef | 
| 757 |  |  |  |  |  |  | # this 'set_from_related' should probably not be called in lots of other | 
| 758 |  |  |  |  |  |  | # situations too, but until that's worked out, kludge it | 
| 759 | 65 | 50 | 33 |  |  | 937 | if ( ( $sub_object || $updates || $might_belong_to || $join_type eq 'LEFT' ) && | 
|  |  |  | 33 |  |  |  |  | 
| 760 |  |  |  |  |  |  | ref $info->{cond} ne 'CODE'  ) { | 
| 761 | 65 |  |  |  |  | 5846 | $object->$name($sub_object); | 
| 762 |  |  |  |  |  |  | } | 
| 763 |  |  |  |  |  |  | } | 
| 764 |  |  |  |  |  |  | else { | 
| 765 |  |  |  |  |  |  | $self->throw_exception( | 
| 766 |  |  |  |  |  |  | "recursive_update doesn't now how to handle relationship '$name' with accessor " . | 
| 767 | 0 |  |  |  |  | 0 | $info->{attrs}{accessor} ); | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  |  | 
| 770 | 109 |  |  |  |  | 321292 | DEBUG and warn "_update_relation end\n"; | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | sub is_m2m { | 
| 774 | 335 |  |  | 335 |  | 902 | my ( $self, $relation ) = @_; | 
| 775 | 335 |  |  |  |  | 1126 | my $rclass = $self->result_class; | 
| 776 |  |  |  |  |  |  |  | 
| 777 |  |  |  |  |  |  | # DBIx::Class::IntrospectableM2M | 
| 778 | 335 | 100 |  |  |  | 5096 | if ( $rclass->can('_m2m_metadata') ) { | 
| 779 | 56 |  |  |  |  | 1680 | return $rclass->_m2m_metadata->{$relation}; | 
| 780 |  |  |  |  |  |  | } | 
| 781 | 279 |  |  |  |  | 915 | my $object = $self->new_result( {} ); | 
| 782 | 279 | 100 | 100 |  |  | 23079 | if ( $object->can($relation) and | 
|  |  |  | 100 |  |  |  |  | 
| 783 |  |  |  |  |  |  | !$self->result_source->has_relationship($relation) and | 
| 784 |  |  |  |  |  |  | $object->can( 'set_' . $relation ) ) { | 
| 785 | 10 |  |  |  |  | 182 | return 1; | 
| 786 |  |  |  |  |  |  | } | 
| 787 | 269 |  |  |  |  | 3790 | return; | 
| 788 |  |  |  |  |  |  | } | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | sub get_m2m_source { | 
| 791 | 7 |  |  | 7 |  | 32 | my ( $self, $relation ) = @_; | 
| 792 | 7 |  |  |  |  | 32 | my $rclass = $self->result_class; | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | # DBIx::Class::IntrospectableM2M | 
| 795 | 7 | 100 |  |  |  | 82 | if ( $rclass->can('_m2m_metadata') ) { | 
| 796 |  |  |  |  |  |  | return $self->result_source->related_source( | 
| 797 |  |  |  |  |  |  | $rclass->_m2m_metadata->{$relation}{relation} ) | 
| 798 | 2 |  |  |  |  | 45 | ->related_source( $rclass->_m2m_metadata->{$relation}{foreign_relation} ); | 
| 799 |  |  |  |  |  |  | } | 
| 800 | 5 |  |  |  |  | 21 | my $object = $self->new_result( {} ); | 
| 801 | 5 |  |  |  |  | 338 | my $r = $object->$relation; | 
| 802 | 5 |  |  |  |  | 18413 | return $r->result_source; | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 |  |  |  |  |  |  | sub _delete_empty_auto_increment { | 
| 806 | 0 |  |  | 0 |  | 0 | my ( $self, $object ) = @_; | 
| 807 | 0 |  |  |  |  | 0 | for my $col ( sort keys %{ $object->{_column_data} } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 808 | 0 | 0 | 0 |  |  | 0 | if ( | 
|  |  |  | 0 |  |  |  |  | 
| 809 |  |  |  |  |  |  | $object->result_source->column_info($col)->{is_auto_increment} and | 
| 810 |  |  |  |  |  |  | ( !defined $object->{_column_data}{$col} or | 
| 811 |  |  |  |  |  |  | $object->{_column_data}{$col} eq '' ) | 
| 812 |  |  |  |  |  |  | ) { | 
| 813 | 0 |  |  |  |  | 0 | delete $object->{_column_data}{$col}; | 
| 814 |  |  |  |  |  |  | } | 
| 815 |  |  |  |  |  |  | } | 
| 816 |  |  |  |  |  |  | } | 
| 817 |  |  |  |  |  |  |  | 
| 818 |  |  |  |  |  |  | sub _get_pk_for_related { | 
| 819 | 80 |  |  | 80 |  | 204 | my ( $self, $relation ) = @_; | 
| 820 | 80 |  |  |  |  | 154 | my $source; | 
| 821 | 80 | 100 |  |  |  | 285 | if ( $self->result_source->has_relationship($relation) ) { | 
| 822 | 73 |  |  |  |  | 477 | $source = $self->result_source->related_source($relation); | 
| 823 |  |  |  |  |  |  | } | 
| 824 |  |  |  |  |  |  |  | 
| 825 |  |  |  |  |  |  | # many to many case | 
| 826 | 80 | 100 |  |  |  | 12073 | if ( is_m2m( $self, $relation ) ) { | 
| 827 | 7 |  |  |  |  | 152 | $source = get_m2m_source( $self, $relation ); | 
| 828 |  |  |  |  |  |  | } | 
| 829 | 80 |  |  |  |  | 2361 | return $source->primary_columns; | 
| 830 |  |  |  |  |  |  | } | 
| 831 |  |  |  |  |  |  |  | 
| 832 |  |  |  |  |  |  | # This function determines whether a relationship should be done before or | 
| 833 |  |  |  |  |  |  | # after the row is inserted into the database | 
| 834 |  |  |  |  |  |  | # relationships before: belongs_to | 
| 835 |  |  |  |  |  |  | # relationships after: has_many, might_have and has_one | 
| 836 |  |  |  |  |  |  | # true means before, false after | 
| 837 |  |  |  |  |  |  | sub _master_relation_cond { | 
| 838 | 112 |  |  | 112 |  | 310 | my ( $self, $name ) = @_; | 
| 839 |  |  |  |  |  |  |  | 
| 840 | 112 |  |  |  |  | 340 | my $source = $self->result_source; | 
| 841 | 112 |  |  |  |  | 327 | my $info   = $source->relationship_info($name); | 
| 842 |  |  |  |  |  |  |  | 
| 843 |  |  |  |  |  |  | # has_many rels are always after | 
| 844 |  |  |  |  |  |  | return 0 | 
| 845 | 112 | 100 |  |  |  | 855 | if $info->{attrs}->{accessor} eq 'multi'; | 
| 846 |  |  |  |  |  |  |  | 
| 847 | 73 |  |  |  |  | 246 | my @foreign_ids = _get_pk_for_related( $self, $name ); | 
| 848 |  |  |  |  |  |  |  | 
| 849 | 73 |  |  |  |  | 665 | my $cond = $info->{cond}; | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | sub _inner { | 
| 852 | 73 |  |  | 73 |  | 206 | my ( $source, $cond, @foreign_ids ) = @_; | 
| 853 |  |  |  |  |  |  |  | 
| 854 | 73 |  |  |  |  | 153 | while ( my ( $f_key, $col ) = each %{$cond} ) { | 
|  | 73 |  |  |  |  | 361 |  | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | # might_have is not master | 
| 857 | 72 |  |  |  |  | 390 | $col   =~ s/^self\.//; | 
| 858 | 72 |  |  |  |  | 279 | $f_key =~ s/^foreign\.//; | 
| 859 | 72 | 100 |  |  |  | 256 | if ( $source->column_info($col)->{is_auto_increment} ) { | 
| 860 | 9 |  |  |  |  | 131 | return 0; | 
| 861 |  |  |  |  |  |  | } | 
| 862 | 63 | 50 |  | 63 |  | 1170 | if ( any { $_ eq $f_key } @foreign_ids ) { | 
|  | 63 |  |  |  |  | 246 |  | 
| 863 | 63 |  |  |  |  | 424 | return 1; | 
| 864 |  |  |  |  |  |  | } | 
| 865 |  |  |  |  |  |  | } | 
| 866 | 1 |  |  |  |  | 4 | return 0; | 
| 867 |  |  |  |  |  |  | } | 
| 868 |  |  |  |  |  |  |  | 
| 869 | 73 | 50 |  |  |  | 251 | if ( ref $cond eq 'HASH' ) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 870 | 73 |  |  |  |  | 216 | return _inner( $source, $cond, @foreign_ids ); | 
| 871 |  |  |  |  |  |  | } | 
| 872 |  |  |  |  |  |  |  | 
| 873 |  |  |  |  |  |  | # arrayref of hashrefs | 
| 874 |  |  |  |  |  |  | elsif ( ref $cond eq 'ARRAY' ) { | 
| 875 | 0 |  |  |  |  | 0 | for my $new_cond (@$cond) { | 
| 876 | 0 |  |  |  |  | 0 | return _inner( $source, $new_cond, @foreign_ids ); | 
| 877 |  |  |  |  |  |  | } | 
| 878 |  |  |  |  |  |  | } | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | # we have a custom join condition, so update afterward | 
| 881 |  |  |  |  |  |  | elsif ( ref $cond eq 'CODE' ) { | 
| 882 | 0 |  |  |  |  | 0 | return 0; | 
| 883 |  |  |  |  |  |  | } | 
| 884 |  |  |  |  |  |  |  | 
| 885 |  |  |  |  |  |  | else { | 
| 886 | 0 |  |  |  |  | 0 | $source->throw_exception( "unhandled relation condition " . ref($cond) ); | 
| 887 |  |  |  |  |  |  | } | 
| 888 | 0 |  |  |  |  | 0 | return; | 
| 889 |  |  |  |  |  |  | } | 
| 890 |  |  |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | 1; | 
| 892 |  |  |  |  |  |  |  | 
| 893 |  |  |  |  |  |  | __END__ | 
| 894 |  |  |  |  |  |  |  | 
| 895 |  |  |  |  |  |  | =pod | 
| 896 |  |  |  |  |  |  |  | 
| 897 |  |  |  |  |  |  | =encoding UTF-8 | 
| 898 |  |  |  |  |  |  |  | 
| 899 |  |  |  |  |  |  | =head1 NAME | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | DBIx::Class::ResultSet::RecursiveUpdate - like update_or_create - but recursive | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | =head1 VERSION | 
| 904 |  |  |  |  |  |  |  | 
| 905 |  |  |  |  |  |  | version 0.41 | 
| 906 |  |  |  |  |  |  |  | 
| 907 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 908 |  |  |  |  |  |  |  | 
| 909 |  |  |  |  |  |  | # The functional interface: | 
| 910 |  |  |  |  |  |  |  | 
| 911 |  |  |  |  |  |  | my $schema = MyDB::Schema->connect(); | 
| 912 |  |  |  |  |  |  | my $new_item = DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update( | 
| 913 |  |  |  |  |  |  | resultset => $schema->resultset('User'), | 
| 914 |  |  |  |  |  |  | updates => { | 
| 915 |  |  |  |  |  |  | id => 1, | 
| 916 |  |  |  |  |  |  | owned_dvds => [ | 
| 917 |  |  |  |  |  |  | { | 
| 918 |  |  |  |  |  |  | title => "One Flew Over the Cuckoo's Nest" | 
| 919 |  |  |  |  |  |  | } | 
| 920 |  |  |  |  |  |  | ] | 
| 921 |  |  |  |  |  |  | }, | 
| 922 |  |  |  |  |  |  | unknown_params_ok => 1, | 
| 923 |  |  |  |  |  |  | ); | 
| 924 |  |  |  |  |  |  |  | 
| 925 |  |  |  |  |  |  |  | 
| 926 |  |  |  |  |  |  | # As ResultSet subclass: | 
| 927 |  |  |  |  |  |  |  | 
| 928 |  |  |  |  |  |  | __PACKAGE__->load_namespaces( default_resultset_class => '+DBIx::Class::ResultSet::RecursiveUpdate' ); | 
| 929 |  |  |  |  |  |  |  | 
| 930 |  |  |  |  |  |  | # in the Schema file (see t/lib/DBSchema.pm).  Or appropriate 'use base' in the ResultSet classes. | 
| 931 |  |  |  |  |  |  |  | 
| 932 |  |  |  |  |  |  | my $user = $schema->resultset('User')->recursive_update({ | 
| 933 |  |  |  |  |  |  | id => 1, | 
| 934 |  |  |  |  |  |  | owned_dvds => [ | 
| 935 |  |  |  |  |  |  | { | 
| 936 |  |  |  |  |  |  | title => "One Flew Over the Cuckoo's Nest" | 
| 937 |  |  |  |  |  |  | } | 
| 938 |  |  |  |  |  |  | ] | 
| 939 |  |  |  |  |  |  | }, { | 
| 940 |  |  |  |  |  |  | unknown_params_ok => 1, | 
| 941 |  |  |  |  |  |  | }); | 
| 942 |  |  |  |  |  |  |  | 
| 943 |  |  |  |  |  |  | # You'll get a warning if you pass non-result specific data to | 
| 944 |  |  |  |  |  |  | # recursive_update. See L</"Additional data in the updates hashref"> | 
| 945 |  |  |  |  |  |  | # for more information how to prevent this. | 
| 946 |  |  |  |  |  |  |  | 
| 947 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | You can feed the ->create method of DBIx::Class with a recursive datastructure | 
| 950 |  |  |  |  |  |  | and have the related records created. Unfortunately you cannot do a similar | 
| 951 |  |  |  |  |  |  | thing with update_or_create. This module tries to fill that void until | 
| 952 |  |  |  |  |  |  | L<DBIx::Class> has an api itself. | 
| 953 |  |  |  |  |  |  |  | 
| 954 |  |  |  |  |  |  | The functional interface can be used without modifications of the model, | 
| 955 |  |  |  |  |  |  | for example by form processors like L<HTML::FormHandler::Model::DBIC>. | 
| 956 |  |  |  |  |  |  |  | 
| 957 |  |  |  |  |  |  | It is a base class for L<DBIx::Class::ResultSet>s providing the method | 
| 958 |  |  |  |  |  |  | recursive_update which works just like update_or_create but can recursively | 
| 959 |  |  |  |  |  |  | update or create result objects composed of multiple rows. All rows need to be | 
| 960 |  |  |  |  |  |  | identified by primary keys so you need to provide them in the update structure | 
| 961 |  |  |  |  |  |  | (unless they can be deduced from the parent row. For example a related row of | 
| 962 |  |  |  |  |  |  | a belongs_to relationship). If any of the primary key columns are missing, | 
| 963 |  |  |  |  |  |  | a new row will be created, with the expectation that the missing columns will | 
| 964 |  |  |  |  |  |  | be filled by it (as in the case of auto_increment primary keys). | 
| 965 |  |  |  |  |  |  |  | 
| 966 |  |  |  |  |  |  | If the resultset itself stores an assignment for the primary key, | 
| 967 |  |  |  |  |  |  | like in the case of: | 
| 968 |  |  |  |  |  |  |  | 
| 969 |  |  |  |  |  |  | my $restricted_rs = $user_rs->search( { id => 1 } ); | 
| 970 |  |  |  |  |  |  |  | 
| 971 |  |  |  |  |  |  | you need to inform recursive_update about the additional predicate with the fixed_fields attribute: | 
| 972 |  |  |  |  |  |  |  | 
| 973 |  |  |  |  |  |  | my $user = $restricted_rs->recursive_update( { | 
| 974 |  |  |  |  |  |  | owned_dvds => [ | 
| 975 |  |  |  |  |  |  | { | 
| 976 |  |  |  |  |  |  | title => 'One Flew Over the Cuckoo's Nest' | 
| 977 |  |  |  |  |  |  | } | 
| 978 |  |  |  |  |  |  | ] | 
| 979 |  |  |  |  |  |  | }, | 
| 980 |  |  |  |  |  |  | { | 
| 981 |  |  |  |  |  |  | fixed_fields => [ 'id' ], | 
| 982 |  |  |  |  |  |  | } | 
| 983 |  |  |  |  |  |  | ); | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | For a many_to_many (pseudo) relation you can supply a list of primary keys | 
| 986 |  |  |  |  |  |  | from the other table and it will link the record at hand to those and | 
| 987 |  |  |  |  |  |  | only those records identified by them. This is convenient for handling web | 
| 988 |  |  |  |  |  |  | forms with check boxes (or a select field with multiple choice) that lets you | 
| 989 |  |  |  |  |  |  | update such (pseudo) relations. | 
| 990 |  |  |  |  |  |  |  | 
| 991 |  |  |  |  |  |  | For a description how to set up base classes for ResultSets see | 
| 992 |  |  |  |  |  |  | L<DBIx::Class::Schema/load_namespaces>. | 
| 993 |  |  |  |  |  |  |  | 
| 994 |  |  |  |  |  |  | =head2 Additional data in the updates hashref | 
| 995 |  |  |  |  |  |  |  | 
| 996 |  |  |  |  |  |  | If you pass additional data to recursive_update which doesn't match a column | 
| 997 |  |  |  |  |  |  | name, column accessor, relationship or many-to-many helper accessor, it will | 
| 998 |  |  |  |  |  |  | throw a warning by default. To disable this behaviour you can set the | 
| 999 |  |  |  |  |  |  | unknown_params_ok attribute to a true value. | 
| 1000 |  |  |  |  |  |  |  | 
| 1001 |  |  |  |  |  |  | The warning thrown is: | 
| 1002 |  |  |  |  |  |  | "No such column, relationship, many-to-many helper accessor or generic accessor '$key'" | 
| 1003 |  |  |  |  |  |  |  | 
| 1004 |  |  |  |  |  |  | When used by L<HTML::FormHandler::Model::DBIC> this can happen if you have | 
| 1005 |  |  |  |  |  |  | additional form fields that aren't relevant to the database but don't have the | 
| 1006 |  |  |  |  |  |  | noupdate attribute set to a true value. | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 |  |  |  |  |  |  | NOTE: in a future version this behaviour will change and throw an exception | 
| 1009 |  |  |  |  |  |  | instead of a warning! | 
| 1010 |  |  |  |  |  |  |  | 
| 1011 |  |  |  |  |  |  | =head1 DESIGN CHOICES | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 |  |  |  |  |  |  | Columns and relationships which are excluded from the updates hashref aren't | 
| 1014 |  |  |  |  |  |  | touched at all. | 
| 1015 |  |  |  |  |  |  |  | 
| 1016 |  |  |  |  |  |  | =head2 Treatment of belongs_to relations | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | In case the relationship is included but undefined in the updates hashref, | 
| 1019 |  |  |  |  |  |  | all columns forming the relationship will be set to null. | 
| 1020 |  |  |  |  |  |  | If not all of them are nullable, DBIx::Class will throw an error. | 
| 1021 |  |  |  |  |  |  |  | 
| 1022 |  |  |  |  |  |  | Updating the relationship: | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1025 |  |  |  |  |  |  | id    => 1, | 
| 1026 |  |  |  |  |  |  | owner => $user->id, | 
| 1027 |  |  |  |  |  |  | }); | 
| 1028 |  |  |  |  |  |  |  | 
| 1029 |  |  |  |  |  |  | Clearing the relationship (only works if cols are nullable!): | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1032 |  |  |  |  |  |  | id    => 1, | 
| 1033 |  |  |  |  |  |  | owner => undef, | 
| 1034 |  |  |  |  |  |  | }); | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 |  |  |  |  |  |  | Updating a relationship including its (full) primary key: | 
| 1037 |  |  |  |  |  |  |  | 
| 1038 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1039 |  |  |  |  |  |  | id    => 1, | 
| 1040 |  |  |  |  |  |  | owner => { | 
| 1041 |  |  |  |  |  |  | id   => 2, | 
| 1042 |  |  |  |  |  |  | name => "George", | 
| 1043 |  |  |  |  |  |  | }, | 
| 1044 |  |  |  |  |  |  | }); | 
| 1045 |  |  |  |  |  |  |  | 
| 1046 |  |  |  |  |  |  | =head2 Treatment of might_have relationships | 
| 1047 |  |  |  |  |  |  |  | 
| 1048 |  |  |  |  |  |  | In case the relationship is included but undefined in the updates hashref, | 
| 1049 |  |  |  |  |  |  | all columns forming the relationship will be set to null. | 
| 1050 |  |  |  |  |  |  |  | 
| 1051 |  |  |  |  |  |  | Updating the relationship: | 
| 1052 |  |  |  |  |  |  |  | 
| 1053 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1054 |  |  |  |  |  |  | id => 1, | 
| 1055 |  |  |  |  |  |  | address => { | 
| 1056 |  |  |  |  |  |  | street => "101 Main Street", | 
| 1057 |  |  |  |  |  |  | city   => "Podunk", | 
| 1058 |  |  |  |  |  |  | state  => "New York", | 
| 1059 |  |  |  |  |  |  | } | 
| 1060 |  |  |  |  |  |  | }); | 
| 1061 |  |  |  |  |  |  |  | 
| 1062 |  |  |  |  |  |  | Clearing the relationship: | 
| 1063 |  |  |  |  |  |  |  | 
| 1064 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1065 |  |  |  |  |  |  | id => 1, | 
| 1066 |  |  |  |  |  |  | address => undef, | 
| 1067 |  |  |  |  |  |  | }); | 
| 1068 |  |  |  |  |  |  |  | 
| 1069 |  |  |  |  |  |  | =head2 Treatment of has_many relations | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 |  |  |  |  |  |  | If a relationship key is included in the data structure with a value of undef | 
| 1072 |  |  |  |  |  |  | or an empty array, all existing related rows will be deleted, or their foreign | 
| 1073 |  |  |  |  |  |  | key columns will be set to null. | 
| 1074 |  |  |  |  |  |  |  | 
| 1075 |  |  |  |  |  |  | The exact behaviour depends on the nullability of the foreign key columns and | 
| 1076 |  |  |  |  |  |  | the value of the "if_not_submitted" parameter. The parameter defaults to | 
| 1077 |  |  |  |  |  |  | undefined which neither nullifies nor deletes. | 
| 1078 |  |  |  |  |  |  |  | 
| 1079 |  |  |  |  |  |  | When the array contains elements they are updated if they exist, created when | 
| 1080 |  |  |  |  |  |  | not and deleted if not included. | 
| 1081 |  |  |  |  |  |  |  | 
| 1082 |  |  |  |  |  |  | =head3 All foreign table columns are nullable | 
| 1083 |  |  |  |  |  |  |  | 
| 1084 |  |  |  |  |  |  | In this case recursive_update defaults to nullifying the foreign columns. | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | =head3 Not all foreign table columns are nullable | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 |  |  |  |  |  |  | In this case recursive_update deletes the foreign rows. | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | Updating the relationship: | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | Passing ids: | 
| 1093 |  |  |  |  |  |  |  | 
| 1094 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1095 |  |  |  |  |  |  | id         => 1, | 
| 1096 |  |  |  |  |  |  | owned_dvds => [1, 2], | 
| 1097 |  |  |  |  |  |  | }); | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 |  |  |  |  |  |  | Passing hashrefs: | 
| 1100 |  |  |  |  |  |  |  | 
| 1101 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1102 |  |  |  |  |  |  | id         => 1, | 
| 1103 |  |  |  |  |  |  | owned_dvds => [ | 
| 1104 |  |  |  |  |  |  | { | 
| 1105 |  |  |  |  |  |  | name => 'temp name 1', | 
| 1106 |  |  |  |  |  |  | }, | 
| 1107 |  |  |  |  |  |  | { | 
| 1108 |  |  |  |  |  |  | name => 'temp name 2', | 
| 1109 |  |  |  |  |  |  | }, | 
| 1110 |  |  |  |  |  |  | ], | 
| 1111 |  |  |  |  |  |  | }); | 
| 1112 |  |  |  |  |  |  |  | 
| 1113 |  |  |  |  |  |  | Passing objects: | 
| 1114 |  |  |  |  |  |  |  | 
| 1115 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1116 |  |  |  |  |  |  | id         => 1, | 
| 1117 |  |  |  |  |  |  | owned_dvds => [ $dvd1, $dvd2 ], | 
| 1118 |  |  |  |  |  |  | }); | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  | You can even mix them: | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1123 |  |  |  |  |  |  | id         => 1, | 
| 1124 |  |  |  |  |  |  | owned_dvds => [ 1, { id => 2 } ], | 
| 1125 |  |  |  |  |  |  | }); | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 |  |  |  |  |  |  | Clearing the relationship: | 
| 1128 |  |  |  |  |  |  |  | 
| 1129 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1130 |  |  |  |  |  |  | id         => 1, | 
| 1131 |  |  |  |  |  |  | owned_dvds => undef, | 
| 1132 |  |  |  |  |  |  | }); | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | This is the same as passing an empty array: | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 |  |  |  |  |  |  | my $user = $user_rs->recursive_update( { | 
| 1137 |  |  |  |  |  |  | id         => 1, | 
| 1138 |  |  |  |  |  |  | owned_dvds => [], | 
| 1139 |  |  |  |  |  |  | }); | 
| 1140 |  |  |  |  |  |  |  | 
| 1141 |  |  |  |  |  |  | =head2 Treatment of many-to-many pseudo relations | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | If a many-to-many accessor key is included in the data structure with a value | 
| 1144 |  |  |  |  |  |  | of undef or an empty array, all existing related rows are unlinked. | 
| 1145 |  |  |  |  |  |  |  | 
| 1146 |  |  |  |  |  |  | When the array contains elements they are updated if they exist, created when | 
| 1147 |  |  |  |  |  |  | not and deleted if not included. | 
| 1148 |  |  |  |  |  |  |  | 
| 1149 |  |  |  |  |  |  | RecursiveUpdate defaults to | 
| 1150 |  |  |  |  |  |  | calling 'set_$rel' to update many-to-many relationships. | 
| 1151 |  |  |  |  |  |  | See L<DBIx::Class::Relationship/many_to_many> for details. | 
| 1152 |  |  |  |  |  |  | set_$rel effectively removes and re-adds all relationship data, | 
| 1153 |  |  |  |  |  |  | even if the set of related items did not change at all. | 
| 1154 |  |  |  |  |  |  |  | 
| 1155 |  |  |  |  |  |  | If L<DBIx::Class::IntrospectableM2M> is in use, RecursiveUpdate will | 
| 1156 |  |  |  |  |  |  | look up the corresponding has_many relationship and use this to recursively | 
| 1157 |  |  |  |  |  |  | update the many-to-many relationship. | 
| 1158 |  |  |  |  |  |  |  | 
| 1159 |  |  |  |  |  |  | While both mechanisms have the same final result, deleting and re-adding | 
| 1160 |  |  |  |  |  |  | all relationship data can have unwanted consequences if triggers or | 
| 1161 |  |  |  |  |  |  | method modifiers are defined or logging modules like L<DBIx::Class::AuditLog> | 
| 1162 |  |  |  |  |  |  | are in use. | 
| 1163 |  |  |  |  |  |  |  | 
| 1164 |  |  |  |  |  |  | The traditional "set_$rel" behaviour can be forced by passing | 
| 1165 |  |  |  |  |  |  | "m2m_force_set_rel => 1" to recursive_update. | 
| 1166 |  |  |  |  |  |  |  | 
| 1167 |  |  |  |  |  |  | See L</is_m2m> for many-to-many pseudo relationship detection. | 
| 1168 |  |  |  |  |  |  |  | 
| 1169 |  |  |  |  |  |  | Updating the relationship: | 
| 1170 |  |  |  |  |  |  |  | 
| 1171 |  |  |  |  |  |  | Passing ids: | 
| 1172 |  |  |  |  |  |  |  | 
| 1173 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1174 |  |  |  |  |  |  | id   => 1, | 
| 1175 |  |  |  |  |  |  | tags => [1, 2], | 
| 1176 |  |  |  |  |  |  | }); | 
| 1177 |  |  |  |  |  |  |  | 
| 1178 |  |  |  |  |  |  | Passing hashrefs: | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1181 |  |  |  |  |  |  | id   => 1, | 
| 1182 |  |  |  |  |  |  | tags => [ | 
| 1183 |  |  |  |  |  |  | { | 
| 1184 |  |  |  |  |  |  | id   => 1, | 
| 1185 |  |  |  |  |  |  | file => 'file0' | 
| 1186 |  |  |  |  |  |  | }, | 
| 1187 |  |  |  |  |  |  | { | 
| 1188 |  |  |  |  |  |  | id   => 2, | 
| 1189 |  |  |  |  |  |  | file => 'file1', | 
| 1190 |  |  |  |  |  |  | }, | 
| 1191 |  |  |  |  |  |  | ], | 
| 1192 |  |  |  |  |  |  | }); | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 |  |  |  |  |  |  | Passing objects: | 
| 1195 |  |  |  |  |  |  |  | 
| 1196 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1197 |  |  |  |  |  |  | id   => 1, | 
| 1198 |  |  |  |  |  |  | tags => [ $tag1, $tag2 ], | 
| 1199 |  |  |  |  |  |  | }); | 
| 1200 |  |  |  |  |  |  |  | 
| 1201 |  |  |  |  |  |  | You can even mix them: | 
| 1202 |  |  |  |  |  |  |  | 
| 1203 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1204 |  |  |  |  |  |  | id   => 1, | 
| 1205 |  |  |  |  |  |  | tags => [ 2, { id => 3 } ], | 
| 1206 |  |  |  |  |  |  | }); | 
| 1207 |  |  |  |  |  |  |  | 
| 1208 |  |  |  |  |  |  | Clearing the relationship: | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1211 |  |  |  |  |  |  | id   => 1, | 
| 1212 |  |  |  |  |  |  | tags => undef, | 
| 1213 |  |  |  |  |  |  | }); | 
| 1214 |  |  |  |  |  |  |  | 
| 1215 |  |  |  |  |  |  | This is the same as passing an empty array: | 
| 1216 |  |  |  |  |  |  |  | 
| 1217 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1218 |  |  |  |  |  |  | id   => 1, | 
| 1219 |  |  |  |  |  |  | tags => [], | 
| 1220 |  |  |  |  |  |  | }); | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 |  |  |  |  |  |  | Make sure that set_$rel used to update many-to-many relationships | 
| 1223 |  |  |  |  |  |  | even if IntrospectableM2M is loaded: | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 |  |  |  |  |  |  | my $dvd = $dvd_rs->recursive_update( { | 
| 1226 |  |  |  |  |  |  | id   => 1, | 
| 1227 |  |  |  |  |  |  | tags => [1, 2], | 
| 1228 |  |  |  |  |  |  | }, | 
| 1229 |  |  |  |  |  |  | { m2m_force_set_rel => 1 }, | 
| 1230 |  |  |  |  |  |  | ); | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 |  |  |  |  |  |  | =head1 INTERFACE | 
| 1233 |  |  |  |  |  |  |  | 
| 1234 |  |  |  |  |  |  | =head1 METHODS | 
| 1235 |  |  |  |  |  |  |  | 
| 1236 |  |  |  |  |  |  | =head2 recursive_update | 
| 1237 |  |  |  |  |  |  |  | 
| 1238 |  |  |  |  |  |  | The method that does the work here. | 
| 1239 |  |  |  |  |  |  |  | 
| 1240 |  |  |  |  |  |  | =head2 is_m2m | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 |  |  |  |  |  |  | =over 4 | 
| 1243 |  |  |  |  |  |  |  | 
| 1244 |  |  |  |  |  |  | =item Arguments: $name | 
| 1245 |  |  |  |  |  |  |  | 
| 1246 |  |  |  |  |  |  | =item Return Value: true, if $name is a many to many pseudo-relationship | 
| 1247 |  |  |  |  |  |  |  | 
| 1248 |  |  |  |  |  |  | =back | 
| 1249 |  |  |  |  |  |  |  | 
| 1250 |  |  |  |  |  |  | The function gets the information about m2m relations from | 
| 1251 |  |  |  |  |  |  | L<DBIx::Class::IntrospectableM2M>. If it isn't loaded in the ResultSource | 
| 1252 |  |  |  |  |  |  | class, the code relies on the fact: | 
| 1253 |  |  |  |  |  |  |  | 
| 1254 |  |  |  |  |  |  | if($object->can($name) and | 
| 1255 |  |  |  |  |  |  | !$object->result_source->has_relationship($name) and | 
| 1256 |  |  |  |  |  |  | $object->can( 'set_' . $name ) | 
| 1257 |  |  |  |  |  |  | ) | 
| 1258 |  |  |  |  |  |  |  | 
| 1259 |  |  |  |  |  |  | to identify a many to many pseudo relationship. In a similar ugly way the | 
| 1260 |  |  |  |  |  |  | ResultSource of that many to many pseudo relationship is detected. | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 |  |  |  |  |  |  | So if you need many to many pseudo relationship support, it's strongly | 
| 1263 |  |  |  |  |  |  | recommended to load L<DBIx::Class::IntrospectableM2M> in your ResultSource | 
| 1264 |  |  |  |  |  |  | class! | 
| 1265 |  |  |  |  |  |  |  | 
| 1266 |  |  |  |  |  |  | =head2 get_m2m_source | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 |  |  |  |  |  |  | =over 4 | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 |  |  |  |  |  |  | =item Arguments: $name | 
| 1271 |  |  |  |  |  |  |  | 
| 1272 |  |  |  |  |  |  | =item Return Value: $result_source | 
| 1273 |  |  |  |  |  |  |  | 
| 1274 |  |  |  |  |  |  | =back | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 |  |  |  |  |  |  | =head1 CONFIGURATION AND ENVIRONMENT | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 |  |  |  |  |  |  | DBIx::Class::RecursiveUpdate requires no configuration files or environment variables. | 
| 1279 |  |  |  |  |  |  |  | 
| 1280 |  |  |  |  |  |  | =head1 DEPENDENCIES | 
| 1281 |  |  |  |  |  |  |  | 
| 1282 |  |  |  |  |  |  | DBIx::Class | 
| 1283 |  |  |  |  |  |  |  | 
| 1284 |  |  |  |  |  |  | optional but recommended: | 
| 1285 |  |  |  |  |  |  | DBIx::Class::IntrospectableM2M | 
| 1286 |  |  |  |  |  |  |  | 
| 1287 |  |  |  |  |  |  | =head1 INCOMPATIBILITIES | 
| 1288 |  |  |  |  |  |  |  | 
| 1289 |  |  |  |  |  |  | None reported. | 
| 1290 |  |  |  |  |  |  |  | 
| 1291 |  |  |  |  |  |  | =head1 BUGS AND LIMITATIONS | 
| 1292 |  |  |  |  |  |  |  | 
| 1293 |  |  |  |  |  |  | The list of reported bugs can be viewed at L<http://rt.cpan.org/Public/Dist/Display.html?Name=DBIx-Class-ResultSet-RecursiveUpdate>. | 
| 1294 |  |  |  |  |  |  |  | 
| 1295 |  |  |  |  |  |  | Please report any bugs or feature requests to | 
| 1296 |  |  |  |  |  |  | C<bug-DBIx-Class-ResultSet-RecursiveUpdate@rt.cpan.org>, or through the web interface at | 
| 1297 |  |  |  |  |  |  | L<http://rt.cpan.org>. | 
| 1298 |  |  |  |  |  |  |  | 
| 1299 |  |  |  |  |  |  | =head1 AUTHORS | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | =over 4 | 
| 1302 |  |  |  |  |  |  |  | 
| 1303 |  |  |  |  |  |  | =item * | 
| 1304 |  |  |  |  |  |  |  | 
| 1305 |  |  |  |  |  |  | Zbigniew Lukasiak <zby@cpan.org> | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  | =item * | 
| 1308 |  |  |  |  |  |  |  | 
| 1309 |  |  |  |  |  |  | John Napiorkowski <jjnapiork@cpan.org> | 
| 1310 |  |  |  |  |  |  |  | 
| 1311 |  |  |  |  |  |  | =item * | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 |  |  |  |  |  |  | Alexander Hartmaier <abraxxa@cpan.org> | 
| 1314 |  |  |  |  |  |  |  | 
| 1315 |  |  |  |  |  |  | =item * | 
| 1316 |  |  |  |  |  |  |  | 
| 1317 |  |  |  |  |  |  | Gerda Shank <gshank@cpan.org> | 
| 1318 |  |  |  |  |  |  |  | 
| 1319 |  |  |  |  |  |  | =back | 
| 1320 |  |  |  |  |  |  |  | 
| 1321 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 1322 |  |  |  |  |  |  |  | 
| 1323 |  |  |  |  |  |  | This software is copyright (c) 2020 by Zbigniew Lukasiak, John Napiorkowski, Alexander Hartmaier. | 
| 1324 |  |  |  |  |  |  |  | 
| 1325 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 1326 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 1327 |  |  |  |  |  |  |  | 
| 1328 |  |  |  |  |  |  | =cut |