ActsAsList: get_next_in_list() und get_previous_in_list()
authorMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 5 Mar 2013 12:13:01 +0000 (13:13 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 6 Mar 2013 09:23:10 +0000 (10:23 +0100)
SL/DB/Helper/ActsAsList.pm
t/db_helper/acts_as_list.t

index f5bc8c2..566422a 100644 (file)
@@ -3,7 +3,8 @@ package SL::DB::Helper::ActsAsList;
 use strict;
 
 use parent qw(Exporter);
-our @EXPORT = qw(move_position_up move_position_down add_to_list remove_from_list reorder_list configure_acts_as_list);
+our @EXPORT = qw(move_position_up move_position_down add_to_list remove_from_list reorder_list configure_acts_as_list
+                 get_previous_in_list get_next_in_list);
 
 use Carp;
 
@@ -112,6 +113,16 @@ SQL
   return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
 }
 
+sub get_next_in_list {
+  my ($self) = @_;
+  return get_previous_or_next($self, 'next');
+}
+
+sub get_previous_in_list {
+  my ($self) = @_;
+  return get_previous_or_next($self, 'previous');
+}
+
 sub reorder_list {
   my ($class_or_self, @ids) = @_;
 
@@ -245,6 +256,30 @@ SQL
   $self->update_attributes($column => $new_position);
 }
 
+sub get_previous_or_next {
+  my ($self, $direction)  = @_;
+
+  my $asc_desc            = $direction eq 'next' ? 'ASC' : 'DESC';
+  my $comparator          = $direction eq 'next' ? '>'   : '<';
+  my $table               = $self->meta->table;
+  my $column              = column_name($self);
+  my $primary_key_col     = ($self->meta->primary_key)[0];
+  my ($group_by, @values) = get_group_by_where($self);
+  $group_by               = " AND ${group_by}" if $group_by;
+  my $sql                 = <<SQL;
+    SELECT ${primary_key_col}
+    FROM ${table}
+    WHERE (${column} ${comparator} ?)
+      ${group_by}
+    ORDER BY ${column} ${asc_desc}
+    LIMIT 1
+SQL
+
+  my $id = ($self->db->dbh->selectrow_arrayref($sql, undef, $self->$column, @values) || [])->[0];
+
+  return $id ? $self->_get_manager_class->find_by(id => $id) : undef;
+}
+
 sub column_name {
   my ($self) = @_;
   my $column = get_spec(ref $self, 'column_name');
@@ -372,6 +407,16 @@ saved to the database.
 Sets this items positional column to C<-1>, saves it and moves all
 following items up by 1.
 
+=item C<get_previous_in_list>
+
+Fetches the previous item in the list. Returns C<undef> if C<$self> is
+already the first one.
+
+=item C<get_next_in_list>
+
+Fetches the next item in the list. Returns C<undef> if C<$self> is
+already the last one.
+
 =item C<reorder_list @ids>
 
 Re-orders the objects given in C<@ids> by their position in C<@ids> by
index 1262353..0d47bb2 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 44;
+use Test::More tests => 50;
 use Test::Exception;
 
 use strict;
@@ -206,7 +206,14 @@ reset_state();
 $item = get_item(8); $item->remove_from_list; $item->parent_id(3); $item->add_to_list(position => 'first');
 test_positions "add_to_list position 'first' in empty", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 3, 1 ];
 
-
+reset_state();
+$item = get_item(4);
+is($item->get_next_in_list->id,                           5, 'Next of 4 is 5');
+is($item->get_previous_in_list->id,                       3, 'Previous of 4 is 5');
+is($item->get_next_in_list->get_previous_in_list->id,     4, 'Previous of Next of 4 is 4');
+is($item->get_previous_in_list->get_next_in_list->id,     4, 'Next of Previous of 4 is 4');
+is($item->get_next_in_list->get_next_in_list,         undef, 'Next of Next of 4 is undef');
+is($item->get_previous_in_list->get_previous_in_list, undef, 'Previous of Previous of 4 is undef');
 
 # Parametervalidierung
 throws_ok { new_item()->move_position_up   } qr/not.*been.*saved/i, 'move up not saved yet';