]> Dogcows Code - chaz/p5-DBIx-Class-ResultSet-RecursiveUpdate/blob - lib/DBIx/Class/ResultSet/RecursiveUpdate.pm
fixed bug where an object that has already been found by its pk is overwritten by...
[chaz/p5-DBIx-Class-ResultSet-RecursiveUpdate] / lib / DBIx / Class / ResultSet / RecursiveUpdate.pm
1 use strict;
2 use warnings;
3
4 package DBIx::Class::ResultSet::RecursiveUpdate;
5
6 # ABSTRACT: like update_or_create - but recursive
7
8 use base qw(DBIx::Class::ResultSet);
9
10 sub recursive_update {
11 my ( $self, $updates, $attrs ) = @_;
12
13 my $fixed_fields;
14 my $unknown_params_ok;
15
16 # 0.21+ api
17 if ( defined $attrs && ref $attrs eq 'HASH' ) {
18 $fixed_fields = $attrs->{fixed_fields};
19 $unknown_params_ok = $attrs->{unknown_params_ok};
20 }
21
22 # pre 0.21 api
23 elsif ( defined $attrs && ref $attrs eq 'ARRAY' ) {
24 $fixed_fields = $attrs;
25 }
26
27 return
28 DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update(
29 resultset => $self,
30 updates => $updates,
31 fixed_fields => $fixed_fields,
32 unknown_params_ok => $unknown_params_ok,
33 );
34 }
35
36 package DBIx::Class::ResultSet::RecursiveUpdate::Functions;
37 use Carp::Clan qw/^DBIx::Class|^HTML::FormHandler|^Try::Tiny/;
38 use Scalar::Util qw( blessed );
39 use List::MoreUtils qw/ any /;
40
41 sub recursive_update {
42 my %params = @_;
43 my ( $self, $updates, $fixed_fields, $object, $resolved,
44 $if_not_submitted, $unknown_params_ok )
45 = @params{
46 qw/resultset updates fixed_fields object resolved if_not_submitted unknown_params_ok/
47 };
48 $resolved ||= {};
49
50 my $source = $self->result_source;
51
52 # warn 'entering: ' . $source->from();
53 carp 'fixed fields needs to be an array ref'
54 if defined $fixed_fields && ref $fixed_fields ne 'ARRAY';
55
56 if ( blessed($updates) && $updates->isa('DBIx::Class::Row') ) {
57 return $updates;
58 }
59 if ( exists $updates->{id} ) {
60 # warn "finding object by id " . $updates->{id} . "\n";
61 $object = $self->find( $updates->{id}, { key => 'primary' } );
62 # warn "object not found by id\n"
63 # unless defined $object;
64 }
65
66 my %fixed_fields = map { $_ => 1 } @$fixed_fields
67 if $fixed_fields;
68 my @missing =
69 grep { !exists $updates->{$_} && !exists $fixed_fields{$_} }
70 $source->primary_columns;
71 if ( !$object && !scalar @missing ) {
72
73 # warn 'finding by: ' . Dumper( $updates ); use Data::Dumper;
74 $object = $self->find( $updates, { key => 'primary' } );
75 }
76 $updates = { %$updates, %$resolved };
77 @missing =
78 grep { !exists $resolved->{$_} } @missing;
79 if ( !$object && !scalar @missing ) {
80
81 # warn 'finding by +resolved: ' . Dumper( $updates ); use Data::Dumper;
82 $object = $self->find( $updates, { key => 'primary' } );
83 }
84
85 $object = $self->new( {} )
86 unless defined $object;
87
88 # warn Dumper( $updates ); use Data::Dumper;
89 # direct column accessors
90 my %columns;
91
92 # relations that that should be done before the row is inserted into the
93 # database like belongs_to
94 my %pre_updates;
95
96 # relations that that should be done after the row is inserted into the
97 # database like has_many, might_have and has_one
98 my %post_updates;
99 my %other_methods;
100 my %columns_by_accessor = _get_columns_by_accessor($self);
101
102 # warn 'resolved: ' . Dumper( $resolved );
103 # warn 'updates: ' . Dumper( $updates ); use Data::Dumper;
104 # warn 'columns: ' . Dumper( \%columns_by_accessor );
105 for my $name ( keys %$updates ) {
106 # columns
107 if ( exists $columns_by_accessor{$name}
108 && !( $source->has_relationship($name)
109 && ref( $updates->{$name} ) ) )
110 {
111
112 #warn "$name is a column\n";
113 $columns{$name} = $updates->{$name};
114 next;
115 }
116
117 # relationships
118 if ( $source->has_relationship($name) ) {
119 if ( _master_relation_cond( $self, $name ) ) {
120
121 #warn "$name is a pre-update rel\n";
122 $pre_updates{$name} = $updates->{$name};
123 next;
124 }
125 else {
126
127 #warn "$name is a post-update rel\n";
128 $post_updates{$name} = $updates->{$name};
129 next;
130 }
131 }
132
133 # many-to-many helper accessors
134 if ( is_m2m( $self, $name ) ) {
135
136 #warn "$name is a many-to-many helper accessor\n";
137 $other_methods{$name} = $updates->{$name};
138 next;
139 }
140
141 # accessors
142 if ( $object->can($name) && not $source->has_relationship($name) ) {
143
144 #warn "$name is an accessor";
145 $other_methods{$name} = $updates->{$name};
146 next;
147 }
148
149 # unknown
150
151 # don't throw a warning instead of an exception to give users
152 # time to adapt to the new API
153 carp(
154 "No such column, relationship, many-to-many helper accessor or generic accessor '$name'"
155 ) unless $unknown_params_ok;
156
157 #$self->throw_exception(
158 # "No such column, relationship, many-to-many helper accessor or generic accessor '$name'"
159 #);
160 }
161
162 # warn 'other: ' . Dumper( \%other_methods ); use Data::Dumper;
163
164 # first update columns and other accessors
165 # so that later related records can be found
166 for my $name ( keys %columns ) {
167
168 #warn "update col $name\n";
169 $object->$name( $columns{$name} );
170 }
171 for my $name ( keys %other_methods ) {
172
173 #warn "update other $name\n";
174 $object->$name( $updates->{$name} );
175 }
176 for my $name ( keys %pre_updates ) {
177
178 #warn "pre_update $name\n";
179 _update_relation( $self, $name, $pre_updates{$name}, $object,
180 $if_not_submitted );
181 }
182
183 # $self->_delete_empty_auto_increment($object);
184 # don't allow insert to recurse to related objects
185 # do the recursion ourselves
186 # $object->{_rel_in_storage} = 1;
187 #warn "CHANGED: " . $object->is_changed . "\n":
188 #warn "IN STOR: " . $object->in_storage . "\n";
189 $object->update_or_insert if $object->is_changed;
190 $object->discard_changes;
191
192 # updating many_to_many
193 for my $name ( keys %$updates ) {
194 next if exists $columns{$name};
195 my $value = $updates->{$name};
196
197 if ( is_m2m( $self, $name ) ) {
198
199 #warn "update m2m $name\n";
200 my ($pk) = _get_pk_for_related( $self, $name );
201 my @rows;
202 my $result_source = $object->$name->result_source;
203 my @updates;
204 if ( !defined $value ) {
205 next;
206 }
207 elsif ( ref $value ) {
208 @updates = @{$value};
209 }
210 else {
211 @updates = ($value);
212 }
213 for my $elem (@updates) {
214 if ( ref $elem ) {
215 push @rows,
216 recursive_update(
217 resultset => $result_source->resultset,
218 updates => $elem
219 );
220 }
221 else {
222 push @rows,
223 $result_source->resultset->find( { $pk => $elem } );
224 }
225 }
226 my $set_meth = 'set_' . $name;
227 $object->$set_meth( \@rows );
228 }
229 }
230 for my $name ( keys %post_updates ) {
231
232 #warn "post_update $name\n";
233 _update_relation( $self, $name, $post_updates{$name}, $object,
234 $if_not_submitted );
235 }
236 return $object;
237 }
238
239 # returns DBIx::Class::ResultSource::column_info as a hash indexed by column accessor || name
240 sub _get_columns_by_accessor {
241 my $self = shift;
242 my $source = $self->result_source;
243 my %columns;
244 for my $name ( $source->columns ) {
245 my $info = $source->column_info($name);
246 $info->{name} = $name;
247 $columns{ $info->{accessor} || $name } = $info;
248 }
249 return %columns;
250 }
251
252 # Arguments: $rs, $name, $updates, $row, $if_not_submitted
253 sub _update_relation {
254 my ( $self, $name, $updates, $object, $if_not_submitted ) = @_;
255
256 # this should never happen because we're checking the paramters passed to
257 # recursive_update, but just to be sure...
258 $object->throw_exception("No such relationship '$name'")
259 unless $object->has_relationship($name);
260
261 #warn "_update_relation $name: OBJ: " . ref($object) . "\n";
262
263 my $info = $object->result_source->relationship_info($name);
264
265 # get a related resultset without a condition
266 my $related_resultset =
267 $self->related_resultset($name)->result_source->resultset;
268 my $resolved;
269 if ( $self->result_source->can('_resolve_condition') ) {
270 $resolved =
271 $self->result_source->_resolve_condition( $info->{cond}, $name,
272 $object );
273 }
274 else {
275 $self->throw_exception(
276 "result_source must support _resolve_condition");
277 }
278
279 # warn "$name resolved: " . Dumper( $resolved ); use Data::Dumper;
280 $resolved = {}
281 if defined $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION
282 && $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION
283 == $resolved;
284
285 my @rel_cols = keys %{ $info->{cond} };
286 map {s/^foreign\.//} @rel_cols;
287
288 #warn "REL_COLS: " . Dumper(@rel_cols); use Data::Dumper;
289 #my $rel_col_cnt = scalar @rel_cols;
290
291 # find out if all related columns are nullable
292 my $all_fks_nullable = 1;
293 for my $rel_col (@rel_cols) {
294 $all_fks_nullable = 0
295 unless $related_resultset->result_source->column_info($rel_col)
296 ->{is_nullable};
297 }
298
299 $if_not_submitted = $all_fks_nullable ? 'nullify' : 'delete'
300 unless defined $if_not_submitted;
301
302 #warn "\tNULLABLE: $all_fks_nullable ACTION: $if_not_submitted\n";
303
304 #warn "RELINFO for $name: " . Dumper($info); use Data::Dumper;
305
306 # the only valid datatype for a has_many rels is an arrayref
307 if ( $info->{attrs}{accessor} eq 'multi' ) {
308
309 # handle undef like empty arrayref
310 $updates = []
311 unless defined $updates;
312 $self->throw_exception(
313 "data for has_many relationship '$name' must be an arrayref")
314 unless ref $updates eq 'ARRAY';
315
316 my @updated_objs;
317
318 #warn "\tupdating has_many rel '$name' ($rel_col_cnt columns cols)\n";
319 for my $sub_updates ( @{$updates} ) {
320 my $sub_object = recursive_update(
321 resultset => $related_resultset,
322 updates => $sub_updates,
323 resolved => $resolved
324 );
325
326 push @updated_objs, $sub_object;
327 }
328
329 #warn "\tcreated and updated related rows\n";
330
331 my @related_pks = $related_resultset->result_source->primary_columns;
332
333 my $rs_rel_delist = $object->$name;
334
335 # foreign table has a single pk column
336 if ( scalar @related_pks == 1 ) {
337 $rs_rel_delist = $rs_rel_delist->search_rs(
338 { $related_pks[0] =>
339 { -not_in => [ map ( $_->id, @updated_objs ) ] }
340 }
341 );
342 }
343
344 # foreign table has multiple pk columns
345 else {
346 my @cond;
347 for my $obj (@updated_objs) {
348 my %cond_for_obj;
349 for my $col (@related_pks) {
350 $cond_for_obj{$col} = $obj->get_column($col);
351 }
352 push @cond, \%cond_for_obj;
353 }
354
355 # only limit resultset if there are related rows left
356 if ( scalar @cond ) {
357 $rs_rel_delist =
358 $rs_rel_delist->search_rs( { -not => [@cond] } );
359 }
360 }
361
362 #warn "\tCOND: " . Dumper(\%cond);
363 #my $rel_delist_cnt = $rs_rel_delist->count;
364 if ( $if_not_submitted eq 'delete' ) {
365
366 #warn "\tdeleting related rows: $rel_delist_cnt\n";
367 $rs_rel_delist->delete;
368 }
369 elsif ( $if_not_submitted eq 'set_to_null' ) {
370
371 #warn "\tnullifying related rows: $rel_delist_cnt\n";
372 my %update = map { $_ => undef } @rel_cols;
373 $rs_rel_delist->update( \%update );
374 }
375 }
376 elsif ($info->{attrs}{accessor} eq 'single'
377 || $info->{attrs}{accessor} eq 'filter' )
378 {
379
380 #warn "\tupdating rel '$name': $if_not_submitted\n";
381 my $sub_object;
382 if ( ref $updates ) {
383
384 # for might_have relationship
385 if ( $info->{attrs}{accessor} eq 'single'
386 && defined $object->$name )
387 {
388 $sub_object = recursive_update(
389 resultset => $related_resultset,
390 updates => $updates,
391 object => $object->$name
392 );
393 }
394 else {
395 $sub_object = recursive_update(
396 resultset => $related_resultset,
397 updates => $updates,
398 resolved => $resolved
399 );
400 }
401 }
402 else {
403 $sub_object = $related_resultset->find($updates)
404 unless (
405 !$updates
406 && ( exists $info->{attrs}{join_type}
407 && $info->{attrs}{join_type} eq 'LEFT' )
408 );
409 }
410 $object->set_from_related( $name, $sub_object )
411 unless (
412 !$sub_object
413 && !$updates
414 && ( exists $info->{attrs}{join_type}
415 && $info->{attrs}{join_type} eq 'LEFT' )
416 );
417 }
418 else {
419 $self->throw_exception(
420 "recursive_update doesn't now how to handle relationship '$name' with accessor "
421 . $info->{attrs}{accessor} );
422 }
423 }
424
425 sub is_m2m {
426 my ( $self, $relation ) = @_;
427 my $rclass = $self->result_class;
428
429 # DBIx::Class::IntrospectableM2M
430 if ( $rclass->can('_m2m_metadata') ) {
431 return $rclass->_m2m_metadata->{$relation};
432 }
433 my $object = $self->new( {} );
434 if ( $object->can($relation)
435 and !$self->result_source->has_relationship($relation)
436 and $object->can( 'set_' . $relation ) )
437 {
438 return 1;
439 }
440 return;
441 }
442
443 sub get_m2m_source {
444 my ( $self, $relation ) = @_;
445 my $rclass = $self->result_class;
446
447 # DBIx::Class::IntrospectableM2M
448 if ( $rclass->can('_m2m_metadata') ) {
449 return $self->result_source->related_source(
450 $rclass->_m2m_metadata->{$relation}{relation} )
451 ->related_source(
452 $rclass->_m2m_metadata->{$relation}{foreign_relation} );
453 }
454 my $object = $self->new( {} );
455 my $r = $object->$relation;
456 return $r->result_source;
457 }
458
459 sub _delete_empty_auto_increment {
460 my ( $self, $object ) = @_;
461 for my $col ( keys %{ $object->{_column_data} } ) {
462 if ($object->result_source->column_info($col)->{is_auto_increment}
463 and ( !defined $object->{_column_data}{$col}
464 or $object->{_column_data}{$col} eq '' )
465 )
466 {
467 delete $object->{_column_data}{$col};
468 }
469 }
470 }
471
472 sub _get_pk_for_related {
473 my ( $self, $relation ) = @_;
474 my $result_source;
475 if ( $self->result_source->has_relationship($relation) ) {
476 $result_source = $self->result_source->related_source($relation);
477 }
478
479 # many to many case
480 if ( is_m2m( $self, $relation ) ) {
481 $result_source = get_m2m_source( $self, $relation );
482 }
483 return $result_source->primary_columns;
484 }
485
486 # This function determines wheter a relationship should be done before or
487 # after the row is inserted into the database
488 # relationships before: belongs_to
489 # relationships after: has_many, might_have and has_one
490 # true means before, false after
491 sub _master_relation_cond {
492 my ( $self, $name ) = @_;
493
494 my $source = $self->result_source;
495 my $info = $source->relationship_info($name);
496
497 #warn "INFO: " . Dumper($info) . "\n";
498
499 # has_many rels are always after
500 return 0
501 if $info->{attrs}->{accessor} eq 'multi';
502
503 my @foreign_ids = _get_pk_for_related( $self, $name );
504
505 #warn "IDS: " . join(', ', @foreign_ids) . "\n";
506
507 my $cond = $info->{cond};
508
509 sub _inner {
510 my ( $source, $cond, @foreign_ids ) = @_;
511
512 while ( my ( $f_key, $col ) = each %{$cond} ) {
513
514 # might_have is not master
515 $col =~ s/^self\.//;
516 $f_key =~ s/^foreign\.//;
517 if ( $source->column_info($col)->{is_auto_increment} ) {
518 return 0;
519 }
520 if ( any { $_ eq $f_key } @foreign_ids ) {
521 return 1;
522 }
523 }
524 return 0;
525 }
526
527 if ( ref $cond eq 'HASH' ) {
528 return _inner( $source, $cond, @foreign_ids );
529 }
530
531 # arrayref of hashrefs
532 elsif ( ref $cond eq 'ARRAY' ) {
533 for my $new_cond (@$cond) {
534 return _inner( $source, $new_cond, @foreign_ids );
535 }
536 }
537 else {
538 $source->throw_exception(
539 "unhandled relation condition " . ref($cond) );
540 }
541 return;
542 }
543
544 1; # Magic true value required at end of module
545 __END__
546
547 =head1 SYNOPSIS
548
549 # The functional interface:
550
551 my $schema = MyDB::Schema->connect();
552 my $new_item = DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update(
553 resultset => $schema->resultset('User'),
554 updates => {
555 id => 1,
556 owned_dvds => [
557 {
558 title => "One Flew Over the Cuckoo's Nest"
559 }
560 ]
561 },
562 unknown_params_ok => 1,
563 );
564
565
566 # As ResultSet subclass:
567
568 __PACKAGE__->load_namespaces( default_resultset_class => '+DBIx::Class::ResultSet::RecursiveUpdate' );
569
570 # in the Schema file (see t/lib/DBSchema.pm). Or appropriate 'use base' in the ResultSet classes.
571
572 my $user = $schema->resultset('User')->recursive_update({
573 id => 1,
574 owned_dvds => [
575 {
576 title => "One Flew Over the Cuckoo's Nest"
577 }
578 ]
579 }, {
580 unknown_params_ok => 1,
581 });
582
583 # You'll get a warning if you pass non-result specific data to
584 # recursive_update. See L</"Additional data in the updates hashref">
585 # for more information how to prevent this.
586
587 =head1 DESCRIPTION
588
589 This is still experimental.
590
591 You can feed the ->create method of DBIx::Class with a recursive datastructure
592 and have the related records created. Unfortunately you cannot do a similar
593 thing with update_or_create. This module tries to fill that void until
594 L<DBIx::Class> has an api itself.
595
596 The functional interface can be used without modifications of the model,
597 for example by form processors like L<HTML::FormHandler::Model::DBIC>.
598
599 It is a base class for L<DBIx::Class::ResultSet>s providing the method
600 recursive_update which works just like update_or_create but can recursively
601 update or create result objects composed of multiple rows. All rows need to be
602 identified by primary keys so you need to provide them in the update structure
603 (unless they can be deduced from the parent row. For example a related row of
604 a belongs_to relationship). If any of the primary key columns are missing,
605 a new row will be created, with the expectation that the missing columns will
606 be filled by it (as in the case of auto_increment primary keys).
607
608 If the resultset itself stores an assignment for the primary key,
609 like in the case of:
610
611 my $restricted_rs = $user_rs->search( { id => 1 } );
612
613 you need to inform recursive_update about the additional predicate with the fixed_fields attribute:
614
615 my $user = $restricted_rs->recursive_update( {
616 owned_dvds => [
617 {
618 title => 'One Flew Over the Cuckoo's Nest'
619 }
620 ]
621 },
622 {
623 fixed_fields => [ 'id' ],
624 }
625 );
626
627 For a many_to_many (pseudo) relation you can supply a list of primary keys
628 from the other table and it will link the record at hand to those and
629 only those records identified by them. This is convenient for handling web
630 forms with check boxes (or a select field with multiple choice) that lets you
631 update such (pseudo) relations.
632
633 For a description how to set up base classes for ResultSets see
634 L<DBIx::Class::Schema/load_namespaces>.
635
636 =head2 Additional data in the updates hashref
637
638 If you pass additional data to recursive_update which doesn't match a column
639 name, column accessor, relationship or many-to-many helper accessor, it will
640 throw a warning by default. To disable this behaviour you can set the
641 unknown_params_ok attribute to a true value.
642
643 The warning thrown is:
644 "No such column, relationship, many-to-many helper accessor or generic accessor '$key'"
645
646 When used by L<HTML::FormHandler::Model::DBIC> this can happen if you have
647 additional form fields that aren't relevant to the database but don't have the
648 noupdate attribute set to a true value.
649
650 NOTE: in a future version this behaviour will change and throw an exception
651 instead of a warning!
652
653
654 =head1 DESIGN CHOICES
655
656 Columns and relationships which are excluded from the updates hashref aren't
657 touched at all.
658
659 =head2 Treatment of belongs_to relations
660
661 In case the relationship is included but undefined in the updates hashref,
662 all columns forming the relationship will be set to null.
663 If not all of them are nullable, DBIx::Class will throw an error.
664
665 Updating the relationship:
666
667 my $dvd = $dvd_rs->recursive_update( {
668 id => 1,
669 owner => $user->id,
670 });
671
672 Clearing the relationship (only works if cols are nullable!):
673
674 my $dvd = $dvd_rs->recursive_update( {
675 id => 1,
676 owner => undef,
677 });
678
679 =head2 Treatment of might_have relationships
680
681 In case the relationship is included but undefined in the updates hashref,
682 all columns forming the relationship will be set to null.
683
684 Updating the relationship:
685
686 my $user = $user_rs->recursive_update( {
687 id => 1,
688 address => {
689 street => "101 Main Street",
690 city => "Podunk",
691 state => "New York",
692 }
693 });
694
695 Clearing the relationship:
696
697 my $user = $user_rs->recursive_update( {
698 id => 1,
699 address => undef,
700 });
701
702 =head2 Treatment of has_many relations
703
704 If a relationship key is included in the data structure with a value of undef
705 or an empty array, all existing related rows will be deleted, or their foreign
706 key columns will be set to null.
707
708 The exact behaviour depends on the nullability of the foreign key columns and
709 the value of the "if_not_submitted" parameter. The parameter defaults to
710 undefined which neither nullifies nor deletes.
711
712 When the array contains elements they are updated if they exist, created when
713 not and deleted if not included.
714
715 =head3 All foreign table columns are nullable
716
717 In this case recursive_update defaults to nullifying the foreign columns.
718
719 =head3 Not all foreign table columns are nullable
720
721 In this case recursive_update deletes the foreign rows.
722
723 Updating the relationship:
724
725 Passing ids:
726
727 my $user = $user_rs->recursive_update( {
728 id => 1,
729 owned_dvds => [1, 2],
730 });
731
732 Passing hashrefs:
733
734 my $user = $user_rs->recursive_update( {
735 id => 1,
736 owned_dvds => [
737 {
738 name => 'temp name 1',
739 },
740 {
741 name => 'temp name 2',
742 },
743 ],
744 });
745
746 Passing objects:
747
748 my $user = $user_rs->recursive_update( {
749 id => 1,
750 owned_dvds => [ $dvd1, $dvd2 ],
751 });
752
753 You can even mix them:
754
755 my $user = $user_rs->recursive_update( {
756 id => 1,
757 owned_dvds => [ 1, { id => 2 } ],
758 });
759
760 Clearing the relationship:
761
762 my $user = $user_rs->recursive_update( {
763 id => 1,
764 owned_dvds => undef,
765 });
766
767 This is the same as passing an empty array:
768
769 my $user = $user_rs->recursive_update( {
770 id => 1,
771 owned_dvds => [],
772 });
773
774 =head2 Treatment of many-to-many pseudo relations
775
776 If a many-to-many accessor key is included in the data structure with a value
777 of undef or an empty array, all existing related rows are unlinked.
778
779 When the array contains elements they are updated if they exist, created when
780 not and deleted if not included.
781
782 See L</is_m2m> for many-to-many pseudo relationship detection.
783
784 Updating the relationship:
785
786 Passing ids:
787
788 my $dvd = $dvd_rs->recursive_update( {
789 id => 1,
790 tags => [1, 2],
791 });
792
793 Passing hashrefs:
794
795 my $dvd = $dvd_rs->recursive_update( {
796 id => 1,
797 tags => [
798 {
799 id => 1,
800 file => 'file0'
801 },
802 {
803 id => 2,
804 file => 'file1',
805 },
806 ],
807 });
808
809 Passing objects:
810
811 my $dvd = $dvd_rs->recursive_update( {
812 id => 1,
813 tags => [ $tag1, $tag2 ],
814 });
815
816 You can even mix them:
817
818 my $dvd = $dvd_rs->recursive_update( {
819 id => 1,
820 tags => [ 2, { id => 3 } ],
821 });
822
823 Clearing the relationship:
824
825 my $dvd = $dvd_rs->recursive_update( {
826 id => 1,
827 tags => undef,
828 });
829
830 This is the same as passing an empty array:
831
832 my $dvd = $dvd_rs->recursive_update( {
833 id => 1,
834 tags => [],
835 });
836
837
838 =head1 INTERFACE
839
840 =head1 METHODS
841
842 =head2 recursive_update
843
844 The method that does the work here.
845
846 =head2 is_m2m
847
848 =over 4
849
850 =item Arguments: $name
851
852 =item Return Value: true, if $name is a many to many pseudo-relationship
853
854 =back
855
856 The function gets the information about m2m relations from
857 L<DBIx::Class::IntrospectableM2M>. If it isn't loaded in the ResultSource
858 class, the code relies on the fact:
859
860 if($object->can($name) and
861 !$object->result_source->has_relationship($name) and
862 $object->can( 'set_' . $name )
863 )
864
865 to identify a many to many pseudo relationship. In a similar ugly way the
866 ResultSource of that many to many pseudo relationship is detected.
867
868 So if you need many to many pseudo relationship support, it's strongly
869 recommended to load L<DBIx::Class::IntrospectableM2M> in your ResultSource
870 class!
871
872 =head2 get_m2m_source
873
874 =over 4
875
876 =item Arguments: $name
877
878 =item Return Value: $result_source
879
880 =back
881
882 =head1 CONFIGURATION AND ENVIRONMENT
883
884 DBIx::Class::RecursiveUpdate requires no configuration files or environment variables.
885
886 =head1 DEPENDENCIES
887
888 DBIx::Class
889
890 optional but recommended:
891 DBIx::Class::IntrospectableM2M
892
893 =head1 INCOMPATIBILITIES
894
895 None reported.
896
897
898 =head1 BUGS AND LIMITATIONS
899
900 The list of reported bugs can be viewed at L<http://rt.cpan.org/Public/Dist/Display.html?Name=DBIx-Class-ResultSet-RecursiveUpdate>.
901
902 Please report any bugs or feature requests to
903 C<bug-dbix-class-recursiveput@rt.cpan.org>, or through the web interface at
904 L<http://rt.cpan.org>.
This page took 0.07811 seconds and 4 git commands to generate.