use strict;
use parent qw(Exporter);
-our @EXPORT = qw(move_position_up move_position_down);
+our @EXPORT = qw(move_position_up move_position_down reorder_list);
use Carp;
do_move($self, 'down');
}
+sub reorder_list {
+ my ($class_or_self, @ids) = @_;
+
+ return 1 unless @ids;
+
+ my $self = ref($class_or_self) ? $class_or_self : $class_or_self->new;
+ my $column = column_name($self);
+ my $result = $self->db->do_transaction(sub {
+ my $query = qq|UPDATE | . $self->meta->table . qq| SET ${column} = ? WHERE id = ?|;
+ my $sth = $self->db->dbh->prepare($query) || die $self->db->dbh->errstr;
+
+ foreach my $new_position (1 .. scalar(@ids)) {
+ $sth->execute($new_position, $ids[$new_position - 1]) || die $sth->errstr;
+ }
+
+ $sth->finish;
+ });
+
+ return $result;
+}
+
#
# Helper functions
#
sub set_position {
my ($self) = @_;
- if (!defined $self->position) {
- my $max_position = $self->db->dbh->selectrow_arrayref(qq|SELECT COALESCE(max(position), 0) FROM | . $self->meta->table)->[0];
- $self->position($max_position + 1);
+ my $column = column_name($self);
+
+ if (!defined $self->$column) {
+ my $max_position = $self->db->dbh->selectrow_arrayref(qq|SELECT COALESCE(max(${column}), 0) FROM | . $self->meta->table)->[0];
+ $self->$column($max_position + 1);
}
return 1;
sub remove_position {
my ($self) = @_;
+ my $column = column_name($self);
$self->load;
- if (defined $self->position) {
- $self->_get_manager_class->update_all(set => { position => \'position - 1' },
- where => [ position => { gt => $self->position } ]);
+ if (defined $self->$column) {
+ $self->_get_manager_class->update_all(set => { $column => \"${column} - 1" },
+ where => [ $column => { gt => $self->$column } ]);
}
return 1;
sub do_move {
my ($self, $direction) = @_;
+ my $column = column_name($self);
croak "Object has not been saved yet" unless $self->id;
- croak "No position set yet" unless defined $self->position;
+ croak "No position set yet" unless defined $self->$column;
my ($comp_sql, $comp_rdbo, $min_max, $plus_minus) = $direction eq 'up' ? ('<', 'ge', 'max', '+') : ('>', 'le', 'min', '-');
- my $new_position = $self->db->dbh->selectrow_arrayref(qq|SELECT ${min_max}(position) FROM | . $self->meta->table . qq| WHERE position ${comp_sql} | . $self->position)->[0];
+ my $new_position = $self->db->dbh->selectrow_arrayref(qq|SELECT ${min_max}(${column}) FROM | . $self->meta->table . qq| WHERE ${column} ${comp_sql} | . $self->$column)->[0];
return undef unless defined $new_position;
- $self->_get_manager_class->update_all(set => { position => $self->position },
- where => [ position => $new_position ]);
- $self->update_attributes(position => $new_position);
+ $self->_get_manager_class->update_all(set => { $column => $self->$column },
+ where => [ $column => $new_position ]);
+ $self->update_attributes($column => $new_position);
+}
+
+sub column_name {
+ my ($self) = @_;
+ return $self->can('sortkey') ? 'sortkey' : 'position';
}
1;
=head1 NAME
-SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a column I<position>
+SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a
+column I<position> or I<sortkey>
=head1 SYNOPSIS
$obj->delete
This mixin assumes that the mixing package's table contains a column
-called C<position>. This column is set automatically upon saving the
-object if it hasn't been set already. If it hasn't then it will be set
-to the maximum position used in the table plus one.
+called C<position> or C<sortkey> (for legacy tables). This column is
+set automatically upon saving the object if it hasn't been set
+already. If it hasn't then it will be set to the maximum position used
+in the table plus one.
When the object is deleted all positions greater than the object's old
position are decreased by one.
Swaps the object with the object one step below the current one
regarding their sort order by exchanging their C<position> values.
+=item C<reorder_list @ids>
+
+Re-orders the objects given in C<@ids> by their position in C<@ids> by
+updating all of their positional columns. Each element in
+C<@positions> must be the ID of an object. The new position is the
+ID's index inside C<@ids> plus one (meaning the first element's new
+position will be 1 and not 0).
+
+This works by executing SQL "UPDATE" statements directly.
+
+Returns the result of the whole transaction (trueish in case of
+success).
+
+This method can be called both as a class method or an instance
+method.
+
=back
=head1 BUGS