X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FOrdered.pm;h=9f2e253229865efc27a9209f5a6c1243f5b1a9bf;hb=234193451aa53051d0c675f7236ab8fc55f8b6b0;hp=e380fd2f20a8526e6b3ec76f0cffb1b75b65b191;hpb=f045efadb79eb028ec8a2208826d09cbfe755b58;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/Ordered.pm b/lib/DBIx/Class/Ordered.pm index e380fd2..9f2e253 100644 --- a/lib/DBIx/Class/Ordered.pm +++ b/lib/DBIx/Class/Ordered.pm @@ -60,20 +60,20 @@ That's it, now you can change the position of your objects. #!/use/bin/perl use My::Item; - + my $item = My::Item->create({ name=>'Matt S. Trout' }); # If using grouping_column: my $item = My::Item->create({ name=>'Matt S. Trout', group_id=>1 }); - + my $rs = $item->siblings(); my @siblings = $item->siblings(); - + my $sibling; $sibling = $item->first_sibling(); $sibling = $item->last_sibling(); $sibling = $item->previous_sibling(); $sibling = $item->next_sibling(); - + $item->move_previous(); $item->move_next(); $item->move_first(); @@ -272,6 +272,20 @@ sub last_sibling { return defined $lsib ? $lsib : 0; } +# an optimized method to get the last sibling position value without inflating a row object +sub _last_sibling_posval { + my $self = shift; + my $position_column = $self->position_column; + + my $cursor = $self->next_siblings->search( + {}, + { rows => 1, order_by => { '-desc' => $position_column }, select => $position_column }, + )->cursor; + + my ($pos) = $cursor->next; + return $pos; +} + =head2 move_previous $item->move_previous(); @@ -299,7 +313,7 @@ the last in the list. sub move_next { my $self = shift; - return 0 unless $self->next_siblings->count; + return 0 unless defined $self->_last_sibling_posval; # quick way to check for no more siblings return $self->move_to ($self->_position + 1); } @@ -327,7 +341,11 @@ on success, and 0 if the object is already the last one. sub move_last { my $self = shift; - return $self->move_to( $self->_group_rs->count ); + my $last_posval = $self->_last_sibling_posval; + + return 0 unless defined $last_posval; + + return $self->move_to( $self->_position_from_value ($last_posval) ); } =head2 move_to @@ -349,8 +367,9 @@ sub move_to { my $position_column = $self->position_column; - # FIXME this needs to be wrapped in a transaction { + my $guard = $self->result_source->schema->txn_scope_guard; + my ($direction, @between); if ( $from_position < $to_position ) { $direction = -1; @@ -371,6 +390,8 @@ sub move_to { $self->_shift_siblings ($direction, @between); $self->_ordered_internal_update({ $position_column => $new_pos_val }); + $guard->commit; + return 1; } } @@ -412,30 +433,36 @@ sub move_to_group { return $self->move_to ($to_position); } - # FIXME this needs to be wrapped in a transaction { + my $guard = $self->result_source->schema->txn_scope_guard; + # Move to end of current group to adjust siblings $self->move_last; $self->set_inflated_columns({ %$to_group, $position_column => undef }); - my $new_group_count = $self->_group_rs->count; + my $new_group_last_posval = $self->_last_sibling_posval; + my $new_group_last_position = $self->_position_from_value ( + $new_group_last_posval + ); - if ( not defined($to_position) or $to_position > $new_group_count) { + if ( not defined($to_position) or $to_position > $new_group_last_position) { $self->set_column( - $position_column => $new_group_count - ? $self->_next_position_value ( $self->last_sibling->get_column ($position_column) ) # FIXME - no need to inflate last_sibling + $position_column => $new_group_last_position + ? $self->_next_position_value ( $new_group_last_posval ) : $self->_initial_position_value ); } else { my $bumped_pos_val = $self->_position_value ($to_position); - my @between = ($to_position, $new_group_count); + my @between = ($to_position, $new_group_last_position); $self->_shift_siblings (1, @between); #shift right $self->set_column( $position_column => $bumped_pos_val ); } $self->_ordered_internal_update; + $guard->commit; + return 1; } } @@ -453,10 +480,10 @@ sub insert { my $position_column = $self->position_column; unless ($self->get_column($position_column)) { - my $lsib = $self->last_sibling; # FIXME - no need to inflate last_sibling + my $lsib_posval = $self->_last_sibling_posval; $self->set_column( - $position_column => ($lsib - ? $self->_next_position_value ( $lsib->get_column ($position_column) ) + $position_column => (defined $lsib_posval + ? $self->_next_position_value ( $lsib_posval ) : $self->_initial_position_value ) ); @@ -493,8 +520,9 @@ sub update { return $self->next::method( \%changes, @_ ); } - # FIXME this needs to be wrapped in a transaction { + my $guard = $self->result_source->schema->txn_scope_guard; + # if any of our grouping columns have been changed if (grep { exists $changes{$_} } ($self->_grouping_columns) ) { @@ -522,7 +550,20 @@ sub update { $self->move_to(delete $changes{$position_column}); } - return $self->next::method( \%changes, @_ ); + my @res; + my $want = wantarray(); + if (not defined $want) { + $self->next::method( \%changes, @_ ); + } + elsif ($want) { + @res = $self->next::method( \%changes, @_ ); + } + else { + $res[0] = $self->next::method( \%changes, @_ ); + } + + $guard->commit; + return $want ? @res : $res[0]; } } @@ -536,11 +577,25 @@ integrity of the positions. sub delete { my $self = shift; - # FIXME this needs to be wrapped in a transaction - { - $self->move_last; - return $self->next::method( @_ ); + + my $guard = $self->result_source->schema->txn_scope_guard; + + $self->move_last; + + my @res; + my $want = wantarray(); + if (not defined $want) { + $self->next::method( @_ ); + } + elsif ($want) { + @res = $self->next::method( @_ ); } + else { + $res[0] = $self->next::method( @_ ); + } + + $guard->commit; + return $want ? @res : $res[0]; } =head1 METHODS FOR EXTENDING ORDERED @@ -568,6 +623,27 @@ sub _position { return $self->get_column ($self->position_column); } +=head2 _position_from_value + + my $num_pos = $item->_position_of_value ( $pos_value ) + +Returns the B of an object with a B set to C<$pos_value>. By default simply returns C<$pos_value>. + +=cut +sub _position_from_value { + my ($self, $val) = @_; + + return 0 unless defined $val; + +# #the right way to do this +# return $self -> _group_rs +# -> search({ $self->position_column => { '<=', $val } }) +# -> count + + return $val; +} + =head2 _position_value my $pos_value = $item->_position_value ( $pos ) @@ -658,12 +734,22 @@ sub _shift_siblings { # position column is part of a unique constraint, and do a # one-by-one update if this is the case - if (grep { $_ eq $position_column } ( map { @$_ } (values %{{ $self->result_source->unique_constraints }} ) ) ) { + my $rsrc = $self->result_source; + + if (grep { $_ eq $position_column } ( map { @$_ } (values %{{ $rsrc->unique_constraints }} ) ) ) { + + my @pcols = $rsrc->primary_columns; + my $cursor = $shift_rs->search ({}, { order_by => { "-$ord", $position_column }, columns => \@pcols } )->cursor; + my $rs = $self->result_source->resultset; - my $rs = $shift_rs->search ({}, { order_by => { "-$ord", $position_column } } ); - # FIXME - no need to inflate each row - while (my $r = $rs->next) { - $r->_ordered_internal_update ({ $position_column => \ "$position_column $op 1" } ); + while (my @pks = $cursor->next ) { + + my $cond; + for my $i (0.. $#pcols) { + $cond->{$pcols[$i]} = $pks[$i]; + } + + $rs->search($cond)->update ({ $position_column => \ "$position_column $op 1" } ); } } else { @@ -818,7 +904,11 @@ could result in the position not being assigned correctly. =head1 AUTHOR -Aran Deltac + Original code framework + Aran Deltac + + Constraints support and code generalisation + Peter Rabbitson =head1 LICENSE