local copy of DBIC ordered_handle_updates branch
Brian Kirkbride [Tue, 4 Jul 2006 04:38:57 +0000 (04:38 +0000)]
r2107@home:  bkirkbri | 2006-07-03 23:25:35 -0500
Added a new method, move_to_group
r2108@home:  bkirkbri | 2006-07-03 23:27:13 -0500
Overloaded update() to handle changes to position_column or group_column correctly
r2109@home:  bkirkbri | 2006-07-03 23:30:35 -0500
Added tests for new move_to_group method and overloaded update()

lib/DBIx/Class/Ordered.pm
t/87ordered.t

index 8e2c74d..b77cc35 100644 (file)
@@ -290,10 +290,55 @@ sub move_to {
     });
     my $op = ($from_position>$to_position) ? '+' : '-';
     $rs->update({ $position_column => \"$position_column $op 1" });
+    $self->{_ORDERED_INTERNAL_UPDATE} = 1;
     $self->update({ $position_column => $to_position });
     return 1;
 }
 
+=head2 move_to_group
+
+  $item->move_to_group( $group, $position );
+
+Moves the object to the specified position of the specified
+group, or to the end of the group if $position is undef.
+1 is returned on success, and 0 is returned if the object is
+already at the specified position of the specified group.
+
+=cut
+
+sub move_to_group {
+    my( $self, $to_group, $to_position ) = @_;
+    my $position_column = $self->position_column;
+    my $grouping_column = $self->grouping_column;
+
+    return 0 if ( ! defined($to_group) );
+    return 0 if ( defined($to_position) and $to_position < 1 );
+    return 0 if ( $self->$grouping_column==$to_group and defined($to_position) and $self->$position_column==$to_position );
+
+    # Move to end of current group and adjust siblings
+    $self->move_last;
+
+    $self->$grouping_column($to_group);
+    my $new_group_count = $self->result_source->resultset->search({$self->_grouping_clause()})->count();
+    if (!defined($to_position) or $to_position > $new_group_count) {
+        $self->{_ORDERED_INTERNAL_UPDATE} = 1;
+        $self->update({ $position_column => $new_group_count + 1 });
+    }
+    else {
+        my @between = ($to_position, $new_group_count);
+
+        my $rs = $self->result_source->resultset->search({
+            $position_column => { -between => [ @between ] },
+            $self->_grouping_clause(),
+        });
+        $rs->update({ $position_column => \"$position_column + 1" });
+        $self->{_ORDERED_INTERNAL_UPDATE} = 1;
+        $self->update({ $position_column => $to_position });
+    }
+
+    return 1;
+}
+
 =head2 insert
 
 Overrides the DBIC insert() method by providing a default 
@@ -310,6 +355,42 @@ sub insert {
     return $self->next::method( @_ );
 }
 
+=head2 update
+
+Overrides the DBIC update() method by checking for a change
+to the position and/or group columns.  Movement within a
+group or to another group is handled by repositioning
+the appropriate siblings.  Position defaults to the end
+of a new group if it has been changed to undef.
+
+=cut
+
+sub update {
+    my $self = shift;
+
+    if ($self->{_ORDERED_INTERNAL_UPDATE}) {
+        delete $self->{_ORDERED_INTERNAL_UPDATE};
+        return $self->next::method( @_ );
+    }
+
+    $self->set_columns($_[0]) if @_ > 0;
+    my %changes = $self->get_dirty_columns;
+    $self->discard_changes;
+
+    my $pos_col = $self->position_column;
+    my $grp_col = $self->grouping_column;
+    if (defined($grp_col) and exists $changes{$grp_col}) {
+        $self->move_to_group(
+            delete($changes{$grp_col}),
+            exists($changes{$pos_col}) ? delete($changes{$pos_col}) : $self->$pos_col
+        );
+    }
+    elsif (exists $changes{$pos_col}) {
+        $self->move_to(delete $changes{$pos_col});
+    }
+    return $self->next::method( \%changes );
+}
+
 =head2 delete
 
 Overrides the DBIC delete() method by first moving the object 
index b1d484c..7843eee 100644 (file)
@@ -8,7 +8,7 @@ use DBICTest;
 
 my $schema = DBICTest->init_schema();
 
-plan tests => 321;
+plan tests => 417;
 
 my $employees = $schema->resultset('Employee');
 $employees->delete();
@@ -23,20 +23,77 @@ hammer_rs( $employees );
 
 DBICTest::Employee->grouping_column('group_id');
 $employees->delete();
-foreach my $group_id (1..3) {
+foreach my $group_id (1..4) {
     foreach (1..6) {
         $employees->create({ name=>'temp', group_id=>$group_id });
     }
 }
 $employees = $employees->search(undef,{order_by=>'group_id,position'});
 
-foreach my $group_id (1..3) {
+foreach my $group_id (1..4) {
     my $group_employees = $employees->search({group_id=>$group_id});
     $group_employees->all();
     ok( check_rs($group_employees), "group intial positions" );
     hammer_rs( $group_employees );
 }
 
+my $group_3 = $employees->search({group_id=>3});
+my $to_group = 1;
+my $to_pos = undef;
+while (my $employee = $group_3->next) {
+       $employee->move_to_group($to_group, $to_pos);
+       $to_pos++;
+       $to_group = $to_group==1 ? 2 : 1;
+}
+foreach my $group_id (1..4) {
+    my $group_employees = $employees->search({group_id=>$group_id});
+    $group_employees->all();
+    ok( check_rs($group_employees), "group positions after move_to_group" );
+}
+
+my $employee = $employees->search({group_id=>4})->first;
+$employee->position(2);
+$employee->update;
+ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 1" );
+$employee = $employees->search({group_id=>4})->first;
+$employee->update({position=>3});
+ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 2" );
+$employee = $employees->search({group_id=>4})->first;
+$employee->group_id(1);
+$employee->update;
+ok(
+       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+       "overloaded update 3"
+);
+$employee = $employees->search({group_id=>4})->first;
+$employee->update({group_id=>2});
+ok(
+       check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
+       "overloaded update 4"
+);
+$employee = $employees->search({group_id=>4})->first;
+$employee->group_id(1);
+$employee->position(3);
+$employee->update;
+ok(
+       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+       "overloaded update 5"
+);
+$employee = $employees->search({group_id=>4})->first;
+$employee->group_id(2);
+$employee->position(undef);
+$employee->update;
+ok(
+       check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
+       "overloaded update 6"
+);
+$employee = $employees->search({group_id=>4})->first;
+$employee->update({group_id=>1,position=>undef});
+ok(
+       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+       "overloaded update 7"
+);
+
 sub hammer_rs {
     my $rs = shift;
     my $employee;