Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 0fc82c30

Von Moritz Bunkus vor fast 12 Jahren hinzugefügt

  • ID 0fc82c30071666a29a96a706119dff8609e16ef1
  • Vorgänger 8fff964f
  • Nachfolger f12b3596

ActsAsList: Möglichkeit, Positionsgruppen nach anderen Spalten anzugeben

Unterschiede anzeigen:

SL/DB/Helper/ActsAsList.pm
3 3
use strict;
4 4

  
5 5
use parent qw(Exporter);
6
our @EXPORT = qw(move_position_up move_position_down reorder_list);
6
our @EXPORT = qw(move_position_up move_position_down reorder_list configure_acts_as_list);
7 7

  
8 8
use Carp;
9 9

  
10
my %list_spec;
11

  
10 12
sub import {
11 13
  my ($class, @params)   = @_;
12 14
  my $importing = caller();
......
54 56
  return $result;
55 57
}
56 58

  
59
sub configure_acts_as_list {
60
  my ($class, %params) = @_;
61

  
62
  $list_spec{$class} = {
63
    group_by    => $params{group_by},
64
    column_name => $params{column_name},
65
  };
66
}
67

  
57 68
#
58 69
# Helper functions
59 70
#
60 71

  
72
sub get_group_by_where {
73
  my ($self)   = @_;
74

  
75
  my $group_by = get_spec(ref $self, 'group_by') || [];
76
  $group_by    = [ $group_by ] if $group_by && !ref $group_by;
77

  
78
  my @where    = map { my $value = $self->$_; defined($value) ? "(${_} = " . $value . ")" : "(${_} IS NULL)" } @{ $group_by };
79

  
80
  return join ' AND ', @where;
81
}
82

  
61 83
sub set_position {
62 84
  my ($self) = @_;
63 85
  my $column = column_name($self);
64 86

  
65
  if (!defined $self->$column) {
66
    my $max_position = $self->db->dbh->selectrow_arrayref(qq|SELECT COALESCE(max(${column}), 0) FROM | . $self->meta->table)->[0];
67
    $self->$column($max_position + 1);
68
  }
87
  return 1 if defined $self->$column;
88

  
89
  my $table        = $self->meta->table;
90
  my $where        = get_group_by_where($self);
91
  $where           = " WHERE ${where}" if $where;
92
  my $sql = <<SQL;
93
    SELECT COALESCE(max(${column}), 0)
94
    FROM ${table}
95
    ${where}
96
SQL
97

  
98
  my $max_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
99
  $self->$column($max_position + 1);
69 100

  
70 101
  return 1;
71 102
}
......
75 106
  my $column = column_name($self);
76 107

  
77 108
  $self->load;
78
  if (defined $self->$column) {
79
    $self->_get_manager_class->update_all(set   => { $column => \"${column} - 1" },
80
                                          where => [ $column => { gt => $self->$column } ]);
81
  }
109
  return 1 unless defined $self->$column;
110

  
111
  my $table    = $self->meta->table;
112
  my $value    = $self->$column;
113
  my $group_by = get_group_by_where($self);
114
  $group_by    = ' AND ' . $group_by if $group_by;
115
  my $sql      = <<SQL;
116
    UPDATE ${table}
117
    SET ${column} = ${column} - 1
118
    WHERE (${column} > ${value}) ${group_by}
119
SQL
120

  
121
  $self->db->dbh->do($sql);
82 122

  
83 123
  return 1;
84 124
}
......
90 130
  croak "Object has not been saved yet" unless $self->id;
91 131
  croak "No position set yet"           unless defined $self->$column;
92 132

  
93
  my ($comp_sql, $comp_rdbo, $min_max, $plus_minus) = $direction eq 'up' ? ('<', 'ge', 'max', '+') : ('>', 'le', 'min', '-');
133
  my $table                                        = $self->meta->table;
134
  my $old_position                                 = $self->$column;
135
  my ($comp_sel, $comp_upd, $min_max, $plus_minus) = $direction eq 'up' ? ('<', '>=', 'max', '+') : ('>', '<=', 'min', '-');
136
  my $group_by                                     = get_group_by_where($self);
137
  $group_by                                        = ' AND ' . $group_by if $group_by;
138
  my $sql                                          = <<SQL;
139
    SELECT ${min_max}(${column})
140
    FROM ${table}
141
    WHERE (${column} ${comp_sel} ${old_position})
142
      ${group_by}
143
SQL
94 144

  
95
  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];
145
  my $new_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
96 146

  
97 147
  return undef unless defined $new_position;
98 148

  
99
  $self->_get_manager_class->update_all(set   => { $column => $self->$column },
100
                                        where => [ $column => $new_position ]);
149
  $sql = <<SQL;
150
    UPDATE ${table}
151
    SET ${column} = ${old_position}
152
    WHERE (${column} = ${new_position})
153
     ${group_by};
154
SQL
155

  
156
  $self->db->dbh->do($sql);
157

  
101 158
  $self->update_attributes($column => $new_position);
102 159
}
103 160

  
104 161
sub column_name {
105 162
  my ($self) = @_;
163
  my $column = get_spec(ref $self, 'column_name');
164
  return $column if $column;
106 165
  return $self->can('sortkey') ? 'sortkey' : 'position';
107 166
}
108 167

  
168
sub get_spec {
169
  my ($class, $key) = @_;
170

  
171
  return undef unless $list_spec{$class};
172
  return $list_spec{$class}->{$key};
173
}
174

  
109 175
1;
110 176
__END__
111 177

  
......
116 182
=head1 NAME
117 183

  
118 184
SL::DB::Helper::ActsAsList - Mixin for managing ordered items by a
119
column I<position> or I<sortkey>
185
column
120 186

  
121 187
=head1 SYNOPSIS
122 188

  
......
146 212
When the object is deleted all positions greater than the object's old
147 213
position are decreased by one.
148 214

  
149
=head1 FUNCTIONS
215
The column name to use can be configured via L<configure_acts_as_list>.
216

  
217
=head1 CLASS FUNCTIONS
218

  
219
=over 4
220

  
221
=item C<configure_acts_as_list %params>
222

  
223
Configures the mixin's behaviour. C<%params> can contain the following
224
values:
225

  
226
=over 2
227

  
228
=item C<column_name>
229

  
230
The name of the column containing the position. If not set explicitly
231
then the mixin will use C<sortkey> if the model contains such a column
232
(only for legacy tables) and C<position> otherwise.
233

  
234
=item C<group_by>
235

  
236
An optional column name (or array reference of column names) by which
237
to group. If a table contains items for several distinct sets and each
238
set has its own sorting then this can be used.
239

  
240
An example would be requirement spec text blocks. They have a column
241
called C<output_position> that selects where to output the text blocks
242
(either before or after the sections). Furthermore these text blocks
243
each belong to a single requirement spec document. So each combination
244
of C<requirement_spec_id> and C<output_position> should have its own
245
set of C<position> values, which can be achieved by configuring this
246
mixin with C<group_by = [qw(requirement_spec_id output_position)]>.
247

  
248
=back
249

  
250
=back
251

  
252
=head1 INSTANCE FUNCTIONS
150 253

  
151 254
=over 4
152 255

  

Auch abrufbar als: Unified diff