+sub remove_from_list {
+ my ($self) = @_;
+
+ my $worker = sub {
+ remove_position($self);
+
+ # Set to -1 manually because $self->update_attributes() would
+ # trigger the before_save() hook from this very plugin assigning a
+ # number at the end of the list again.
+ my $table = $self->meta->table;
+ my $column = column_name($self);
+ my $primary_key_col = ($self->meta->primary_key)[0];
+ my $sql = <<SQL;
+ UPDATE ${table}
+ SET ${column} = -1
+ WHERE ${primary_key_col} = ?
+SQL
+ $self->db->dbh->do($sql, undef, $self->$primary_key_col);
+ $self->$column(undef);
+ };
+
+ return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
+}
+
+sub add_to_list {
+ my ($self, %params) = @_;
+
+ croak "Invalid parameter 'position'" unless ($params{position} || '') =~ m/^ (?: before | after | first | last ) $/x;
+
+ my $column = column_name($self);
+
+ $self->remove_from_list if ($self->$column // -1) != -1;
+
+ if ($params{position} eq 'last') {
+ set_position($self);
+ $self->save;
+ return;
+ }
+
+ my $table = $self->meta->table;
+ 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 $new_position;
+
+ if ($params{position} eq 'first') {
+ $new_position = 1;
+
+ } else {
+ # Can only be 'before' or 'after' -- 'last' has been checked above
+ # already.
+
+ my $reference = $params{reference};
+ croak "Missing parameter 'reference'" if !$reference;
+
+ my $reference_pos;
+ if (ref $reference) {
+ $reference_pos = $reference->$column;
+ } else {
+ ($reference_pos) = $self->db->dbh->selectrow_array(qq|SELECT ${column} FROM ${table} WHERE ${primary_key_col} = ?|, undef, $reference);
+ }
+
+ $new_position = $params{position} eq 'before' ? $reference_pos : $reference_pos + 1;
+ }
+
+ my $query = <<SQL;
+ UPDATE ${table}
+ SET ${column} = ${column} + 1
+ WHERE (${column} > ?)
+ ${group_by}
+SQL
+
+ my $worker = sub {
+ $self->db->dbh->do($query, undef, $new_position - 1, @values);
+ $self->update_attributes($column => $new_position);
+ };
+
+ 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 get_full_list {
+ my ($self) = @_;
+
+ my $group_by = get_spec(ref $self, 'group_by') || [];
+ $group_by = [ $group_by ] if $group_by && !ref $group_by;
+ my @where = map { ($_ => $self->$_) } @{ $group_by };
+
+ return $self->_get_manager_class->get_all(where => \@where, sort_by => column_name($self) . ' ASC');
+}
+