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