+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');
+}
+