smart | Webentwicklung
Alles rund um HTML5, PHP, WordPress & Co.

Ruby on Rails & acts_as_list für einfache Listensortierung

27. Juni 2012
Stephan
Listensortierung in Ruby on Rails mit acts_as_list

Aktuell stand ich vor dem Problem bzw. der Aufgabe in Ruby on Rails eine einfache Listensortierung zu implementieren, so dass ich die Reihenfolge von Objekten eines bestimmten Typs ändern kann.

Im Prinzip ist das auch selbst schnell erledigt, aber wozu das Rad neu erfinden, wenn es bereits Erweiterungen (Gems) genau für diese Funktionalität gibt. Deshalb möchte ich euch zeigen, wie ihr mit der Ruby-Erweiterung acts_as_list eine solche Listensortierung umsetzen könnt.

Die Aufgabe

Die Aufgabe besteht darin, dass ich ein Fragen-Model namens Question habe und jedem Objekt dieses Models können mehrere Antworten (Answer) zugewiesen werden. Andersherum ist eine Antwort also genau einer Frage zugeordnet. Wir haben hier also eine einfache 1:n Beziehung.

Im Backend der zu entwickelnden Applikation soll der administrierende Benutzer nun die Möglichkeit haben, die Antworten einer Frage per Drag & Drop ordnen zu können, um so eine bestimmte Reihenfolge der Antworten festlegen zu können.

Beispiel:

Ausgangsreihenfolge
1. Antwort A
2. Antwort B
3. Antwort C

per Drag & Drop Antwort A an die 3. Position verschieben
1. Antwort B
2. Antwort C
3. Antwort A

Wie das ganze nun per Drag & Drop auf Clientseite umgesetzt wird, soll uns an dieser Stelle egal sein. Was ich euch hier zeigen will, ist die Umsetzung auf Serverseite.

Installation

Installieren könnt ihr die acts_as_list-Erweiterung wie jedes andere Gem auch.

Die Installationsanweisung findet ihr hier: acts_as_list

Models

Als erstes widmen wir uns den beiden benötigten Models. Dazu erstellen wir uns als erstes das Question-Model:

class Question < ActiveRecord::Base
    has_many :answers, ::order => :position
end

Der zweite Doppelpunkt vor order darf nicht mit übernommen werden. Leider konvertiert WordPress ein normales :o in ein Smiley, weshalb ich zwei Doppelpunkte einfügen musste.

Angemerkt sei, dass ich hier jetzt nicht alle Details des Models (z.B. Validierungen etc.) mit angebe, sondern nur das was für die Umsetzung der Listensortierung benötigt wird.

Wie wir also sehen, haben wir eine has_many-Beziehung zwischen dem Question-Model und dem Answer-Model definiert. Weiterhin geben wir mittels order an, dass die zu einer Frage zugeordneten Antworten geordnet nach deren Position aus der Datenbank geholt werden.

Wer sich mit Assoziationen und deren Umsetzung in Ruby on Rails nicht so gut auskennt, dem kann ich die sehr gute Rails-Guide-Dokumentation dazu empfehlen: A Guide to Active Record Associations

Als nächstes implementieren wir das Answer-Model, dass eine belongs_to-Beziehung zum Question-Model hat:

class Answer < ActiveRecord::Base
    belongs_to :question
    acts_as_list :scope => :question
end

Außerdem kommt hier nun zum ersten mal die acts_as_list-Erweiterung zum Tragen, in dem wir definieren, dass das Answer-Model als Liste agiert und geben als scope das Question-Model an. Durch scope geben wir an, dass beim Sortieren die SQL-Bedingung 'question_id = #{question_id}' im Query vorhanden ist.

Migrationen

Der Vollständigkeit wegen, liste ich hier auch die nötigen Migrationen auf.

Question-Migration:

class CreateQuestions < ActiveRecord::Migration
    def change
        create_table :questions do |t|
            t.timestamps
        end
    end
end

Answer-Migration:

class CreateAnswers < ActiveRecord::Migration
    def change
        create_table :answers do |t|
            t.integer :position
            t.references :question

            t.timestamps
        end
    end
end

Auch hier sei angemerkt, dass ich nicht die Attribute bzw. Spalten, die die Models sonst noch beinhalten würden, mit angebe.

Wichtig ist, dass ihr eine Tabellen-Spalte namens position habt. Generell könnt ihr diese Tabellen-Spalte auch anders benennen, aber die acts_as_list-Erweiterung geht standardmäßig von einer position-Spalte aus.

Anwendung der Listensortierung im Controller

Wir nehmen jetzt mal an, dass der Benutzer nun wie im oben aufgeführten Beispiel die Position einer Antwort per Drag & Drop geändert hat und dadurch ein Request an die update_position-Methode des AnswersController gesendet wird. Als Parameter werden dann die ID der Antwort als auch der Frage sowie die neue Position der Antwort an den Server übermittelt.

Gehen wir von dem obigen Beispiel aus, müssten wir die Position von Antwort A von Position 1 auf Position 3 umändern:

def update_position
    @question = Question.find(params[:question_id])
    @answer = @question.answers.find(params[:id])
    new_position = params[:new_position].to_i

    @answer.insert_at(new_position)
end

Dadurch das wir unser Answer-Model mit acts_as_list ausgestattet haben, können wir auf Objekten des Answer-Models nun verschiedene Methoden zur Listensortierung-/ordnung nutzen. In unserem Beispiel nutzen wir z.B. die Methode insert_at, die dazu dient die Position des aktuellen Answer-Objekts zu aktualisieren.

Nehmen wir also an, dass die Variable new_position eine 2 enthält (in der Datenbank ist Position 2 stellvertretend für Position 3 – Index beginnt also bei 0). Dann aktualisiert Ruby on Rails die Position des aktuellen Objekts und ändert automatisch die Positionen der anderen Antworten, die durch die Änderung betroffen sind.

Natürlich gibt es noch weitere nützliche Methoden, wie z.B. move_to_bottom, welche das aktuelle Objekt ans Ende der Liste ordnet.

Alle vorhanden Instanzmethoden der acts_as_list-Erweiterung findet ihr hier: acts_as_list-Instanzmethoden

Fazit

Als Alternative dazu selbst eine Listensortierung in Ruby on Rails zu implementieren, habt ihr in diesem Artikel gesehen, wie ihr mittels der Ruby-Erweiterung acts_as_list sehr leicht eine solche Funktionalität umsetzen könnt, ohne euch mit den dazugehörigen Datenbank-Anfragen auseinandersetzen zu müssen.

Kennt ihr ähnliche Erweiterungen oder habt ihr selbst schon Erfahrungen mit acts_as_list gemacht?

Kommentare  
0 Kommentare vorhanden
0 Trackbacks/Pingbacks vorhanden
Du bist herzlich eingeladen auch ein Kommentar zu hinterlassen!
Kommentar schreiben

Vielen Dank für dein Kommentar!