From: Moritz Bunkus Date: Wed, 6 Feb 2013 13:53:45 +0000 (+0100) Subject: ActsAsList: Möglichkeit, Positionsgruppen nach anderen Spalten anzugeben X-Git-Tag: release-3.1.0beta1~627 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=0fc82c30071666a29a96a706119dff8609e16ef1;p=kivitendo-erp.git ActsAsList: Möglichkeit, Positionsgruppen nach anderen Spalten anzugeben --- diff --git a/SL/DB/Helper/ActsAsList.pm b/SL/DB/Helper/ActsAsList.pm index ce927bba4..5734c2e6d 100644 --- a/SL/DB/Helper/ActsAsList.pm +++ b/SL/DB/Helper/ActsAsList.pm @@ -3,10 +3,12 @@ package SL::DB::Helper::ActsAsList; 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; +my %list_spec; + sub import { my ($class, @params) = @_; my $importing = caller(); @@ -54,18 +56,47 @@ sub reorder_list { 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 # +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); - 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 = <db->dbh->selectrow_arrayref($sql)->[0]; + $self->$column($max_position + 1); return 1; } @@ -75,10 +106,19 @@ sub remove_position { 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 = < ${value}) ${group_by} +SQL + + $self->db->dbh->do($sql); 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; - 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 = <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; - $self->_get_manager_class->update_all(set => { $column => $self->$column }, - where => [ $column => $new_position ]); + $sql = <db->dbh->do($sql); + $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'; } +sub get_spec { + my ($class, $key) = @_; + + return undef unless $list_spec{$class}; + return $list_spec{$class}->{$key}; +} + 1; __END__ @@ -116,7 +182,7 @@ __END__ =head1 NAME SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a -column I or I +column =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. -=head1 FUNCTIONS +The column name to use can be configured via L. + +=head1 CLASS FUNCTIONS + +=over 4 + +=item C + +Configures the mixin's behaviour. C<%params> can contain the following +values: + +=over 2 + +=item C + +The name of the column containing the position. If not set explicitly +then the mixin will use C if the model contains such a column +(only for legacy tables) and C otherwise. + +=item C + +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 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 and C should have its own +set of C values, which can be achieved by configuring this +mixin with C. + +=back + +=back + +=head1 INSTANCE FUNCTIONS =over 4