Hallo,
um mich ein wenig mit Rails vertraut zu machen, schreibe ich als
»Fingerübung« ein kleines Blogsystem. Das klappt bisher sehr gut, meine
gefühlte Produktivität liegt bereits deutlich über meiner bisherigen mit
PHP, und mit ein wenig Google-Suche bekomme ich überraschenderweise
tatsächlich alles so hin, wie ich es möchte. Doch frage ich mich, ob
mein gewählter Lösungsweg der Rails-»Philosophie« entspricht oder eher
einem wilden Hack gleicht. Ich möchte im folgenden ein Problem und
meine gewählte Lösung darstellen. Über Kommentare zu dieser wäre ich
dankbar.
Die Aufgabe: Blogautoren sollen ihre Posts mit Stichworten taggen
können. Die Eingabe der Stichworte soll Komma-separiert während des
Erstellens des Blogeintrags erfolgen. Weiter soll der Blogeintrag
später noch verändert werden können; in derselben Maske sollen dabei
auch die Stichworte geändert werden können.
Meine Lösung: Zwei Tabellen: entries und tags, die über eine
many-to-many-(n:m)-Relation (»has_and_belongs_to_many :tags« im
Entry-Model und vice versa im Tag-Model) miteinander verbunden sind.
In app/views/entry/new.html.erb habe ich ein Textfeld tags[list]
hinzugefügt:
[app/views/entry/new.html.erb]
<% form_for(@entry) do |f| %>
[…]
Comma separated list of tags: <%= text_field "tags", :list %>
[...] <% end %>
Im controller wandle ich den String dann in einen Array, erstelle ggf.
einen neuen Eintrag in der tags-Tabelle und füge einen Verweis auf den
aktuellen Entry hinzu:
[app/controllers/entry_controller.rb]
[…]
def create
@entry = Entry.new(params[:entry])
@entry.user = User.find(:first, :conditions => [ “id = ?”,
session[:user].id])
respond_to do |format|
if @entry.save
params[:tags][:list].split(", ").each do |tag|
@tag = Tag.find(:first, :conditions => ["name = ?", tag]) ||
@tag = Tag.new
@tag.name = tag
@tag.entries << @entry
@tag.save
end
flash[:notice] = ‘Entry was successfully created.’
[…]
Das Editieren des Eintrags erwies sich als etwas schwieriger, da die
bereits referenzierten Tags ja im Edit-View auftauchen sollten.
Zunächst habe ich im Entry-Model eine neue Klasse Tags erstellt, die
über die zwei Methoden list und list=
verfügte.
[app/models/entry.rb]
[…]
class Tags
def list
@list
end
def list=(value)
@list = value
end
end
Im controller fülle ich eine Instanz dieser Klasse dann einem
komma-separierten String der vorhandenen Tags, so dass sie im View
angezeigt werden können:
[app/controllers/entry_controller.rb]
[…]
def edit
@entry = Entry.find(params[:id])
@tags = Tags.new
@tags.list = @entry.tags.collect { |tag| tag.name }.join(', ')
end
Schließlich lösche ich dann beim update zunächst die vorhandenen
Referenzen des entries auf Tags, um anschließend die nunmehr aktuelle
Liste zu schreiben:
[app/controllers/entry_controller.rb]
[…]
def create
@entry = Entry.new(params[:entry])
@entry.user = User.find(:first, :conditions => [ “id = ?”,
session[:user].id])
respond_to do |format|
if @entry.save
params[:tags][:list].split(", ").each do |tag|
@tag = Tag.find(:first, :conditions => ["name = ?", tag]) ||
@tag = Tag.new
@tag.name = tag
@tag.entries << @entry
@tag.save
end
flash[:notice] = ‘Entry was successfully created.’
[…]
Ein Problem könnte sein, dass ich evtl. Tag»leichen« produzieren kann,
da Tags auch weiterexistieren können, ohne referenziert zu sein. Das
halte ich aber für vernachlässigbar.
Vielen Dank für eure Aufmerksamkeit,
olli
–
Homepage von Oliver Heins GnuPG-Key: gpg --recv-keys 0x9A00D827
GnuPG-Fingerprint: F27A BA8C 1CFB B905 65A8 2544 0F07 B675 9A00 D827
NP: Geoff Berner - Clown and Bard
Please avoid sending me Word or PowerPoint attachments.
See We Can Put an End to Word Attachments - GNU Project - Free Software Foundation