]> wagnertech.de Git - kivitendo-erp.git/commitdiff
ActsAsList: Möglichkeit, Positionsgruppen nach anderen Spalten anzugeben
authorMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 6 Feb 2013 13:53:45 +0000 (14:53 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 8 Feb 2013 11:02:09 +0000 (12:02 +0100)
SL/DB/Helper/ActsAsList.pm

index ce927bba41390be1149bf9f041c80215846a2246..5734c2e6da2a693d3ea871c3de69ccb5ca3550aa 100644 (file)
@@ -3,10 +3,12 @@ package SL::DB::Helper::ActsAsList;
 use strict;
 
 use parent qw(Exporter);
 use strict;
 
 use parent qw(Exporter);
-our @EXPORT = qw(move_position_up move_position_down reorder_list);
+our @EXPORT = qw(move_position_up move_position_down reorder_list configure_acts_as_list);
 
 use Carp;
 
 
 use Carp;
 
+my %list_spec;
+
 sub import {
   my ($class, @params)   = @_;
   my $importing = caller();
 sub import {
   my ($class, @params)   = @_;
   my $importing = caller();
@@ -54,18 +56,47 @@ sub reorder_list {
   return $result;
 }
 
   return $result;
 }
 
+sub configure_acts_as_list {
+  my ($class, %params) = @_;
+
+  $list_spec{$class} = {
+    group_by    => $params{group_by},
+    column_name => $params{column_name},
+  };
+}
+
 #
 # Helper functions
 #
 
 #
 # Helper functions
 #
 
+sub get_group_by_where {
+  my ($self)   = @_;
+
+  my $group_by = get_spec(ref $self, 'group_by') || [];
+  $group_by    = [ $group_by ] if $group_by && !ref $group_by;
+
+  my @where    = map { my $value = $self->$_; defined($value) ? "(${_} = " . $value . ")" : "(${_} IS NULL)" } @{ $group_by };
+
+  return join ' AND ', @where;
+}
+
 sub set_position {
   my ($self) = @_;
   my $column = column_name($self);
 
 sub set_position {
   my ($self) = @_;
   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 if defined $self->$column;
+
+  my $table        = $self->meta->table;
+  my $where        = get_group_by_where($self);
+  $where           = " WHERE ${where}" if $where;
+  my $sql = <<SQL;
+    SELECT COALESCE(max(${column}), 0)
+    FROM ${table}
+    ${where}
+SQL
+
+  my $max_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
+  $self->$column($max_position + 1);
 
   return 1;
 }
 
   return 1;
 }
@@ -75,10 +106,19 @@ sub remove_position {
   my $column = column_name($self);
 
   $self->load;
   my $column = column_name($self);
 
   $self->load;
-  if (defined $self->$column) {
-    $self->_get_manager_class->update_all(set   => { $column => \"${column} - 1" },
-                                          where => [ $column => { gt => $self->$column } ]);
-  }
+  return 1 unless defined $self->$column;
+
+  my $table    = $self->meta->table;
+  my $value    = $self->$column;
+  my $group_by = get_group_by_where($self);
+  $group_by    = ' AND ' . $group_by if $group_by;
+  my $sql      = <<SQL;
+    UPDATE ${table}
+    SET ${column} = ${column} - 1
+    WHERE (${column} > ${value}) ${group_by}
+SQL
+
+  $self->db->dbh->do($sql);
 
   return 1;
 }
 
   return 1;
 }
@@ -90,22 +130,48 @@ sub do_move {
   croak "Object has not been saved yet" unless $self->id;
   croak "No position set yet"           unless defined $self->$column;
 
   croak "Object has not been saved yet" unless $self->id;
   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 $table                                        = $self->meta->table;
+  my $old_position                                 = $self->$column;
+  my ($comp_sel, $comp_upd, $min_max, $plus_minus) = $direction eq 'up' ? ('<', '>=', 'max', '+') : ('>', '<=', 'min', '-');
+  my $group_by                                     = get_group_by_where($self);
+  $group_by                                        = ' AND ' . $group_by if $group_by;
+  my $sql                                          = <<SQL;
+    SELECT ${min_max}(${column})
+    FROM ${table}
+    WHERE (${column} ${comp_sel} ${old_position})
+      ${group_by}
+SQL
 
 
-  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];
+  my $new_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
 
   return undef unless defined $new_position;
 
 
   return undef unless defined $new_position;
 
-  $self->_get_manager_class->update_all(set   => { $column => $self->$column },
-                                        where => [ $column => $new_position ]);
+  $sql = <<SQL;
+    UPDATE ${table}
+    SET ${column} = ${old_position}
+    WHERE (${column} = ${new_position})
+     ${group_by};
+SQL
+
+  $self->db->dbh->do($sql);
+
   $self->update_attributes($column => $new_position);
 }
 
 sub column_name {
   my ($self) = @_;
   $self->update_attributes($column => $new_position);
 }
 
 sub column_name {
   my ($self) = @_;
+  my $column = get_spec(ref $self, 'column_name');
+  return $column if $column;
   return $self->can('sortkey') ? 'sortkey' : 'position';
 }
 
   return $self->can('sortkey') ? 'sortkey' : 'position';
 }
 
+sub get_spec {
+  my ($class, $key) = @_;
+
+  return undef unless $list_spec{$class};
+  return $list_spec{$class}->{$key};
+}
+
 1;
 __END__
 
 1;
 __END__
 
@@ -116,7 +182,7 @@ __END__
 =head1 NAME
 
 SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a
 =head1 NAME
 
 SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a
-column I<position> or I<sortkey>
+column
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
@@ -146,7 +212,44 @@ in the table plus one.
 When the object is deleted all positions greater than the object's old
 position are decreased by one.
 
 When the object is deleted all positions greater than the object's old
 position are decreased by one.
 
-=head1 FUNCTIONS
+The column name to use can be configured via L<configure_acts_as_list>.
+
+=head1 CLASS FUNCTIONS
+
+=over 4
+
+=item C<configure_acts_as_list %params>
+
+Configures the mixin's behaviour. C<%params> can contain the following
+values:
+
+=over 2
+
+=item C<column_name>
+
+The name of the column containing the position. If not set explicitly
+then the mixin will use C<sortkey> if the model contains such a column
+(only for legacy tables) and C<position> otherwise.
+
+=item C<group_by>
+
+An optional column name (or array reference of column names) by which
+to group. If a table contains items for several distinct sets and each
+set has its own sorting then this can be used.
+
+An example would be requirement spec text blocks. They have a column
+called C<output_position> that selects where to output the text blocks
+(either before or after the sections). Furthermore these text blocks
+each belong to a single requirement spec document. So each combination
+of C<requirement_spec_id> and C<output_position> should have its own
+set of C<position> values, which can be achieved by configuring this
+mixin with C<group_by = [qw(requirement_spec_id output_position)]>.
+
+=back
+
+=back
+
+=head1 INSTANCE FUNCTIONS
 
 =over 4
 
 
 =over 4