About

This page contains a single entry from the blog posted on October 18, 2007 9:22 PM.

The previous post in this blog was Von befreienden Einschränkungen.

The next post in this blog is Die Geschichte mit der Vererbung.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.31

« Von befreienden Einschränkungen | Main | Die Geschichte mit der Vererbung »

ActiveRecord für Fortgeschrittenere

Neulich habe ich im Buch "Agile Web Development with Rails" eine ganz nette Sache entdeckt, die mir für die Benutzer-Rechteverwaltung sehr gelegen kommt. Ich habe die Entitäten User, Group und AccessRight, wobei sowohl Benutzer als auch Gruppen beliebig viele Zugriffsrechte haben können (habtm). Üblicherweise würde man zur Erstellung der Rechte-Beziehungen einfache Join-Tables mit zwei Fremdschlüsseln verwenden (users_access_rights(user_id, access_right_id)). Da zusätzlich aber gespeichert werden soll, wer wann von wem welche Rechte zugewiesen bekommen hat, macht es Sinn, eine zusätzliche Entität UserAccessRight (dito für Gruppen) zu erstellen. Klassischerweise würde das dann so aussehen:


class User < ActiveRecord::Base
  belongs_to :user_access_right
end
# dito für Group...

class AccessRight < ActiveRecord::Base
  belongs_to :user
end

class UserAccessRight < ActiveRecord::Base
  has_one :user
  has_one :access_right
end

# Liste alle Rechte von Benutzer 1 auf:
user = User.find 1
user.user_access_rights.each do |acc|
  p acc.right.name
end

# darf der Benutzer also Benutzer hinzufügen?
if user.user_access_rights.find {|r| r.access_right_name == 'add_user'}
  p "Benutzer #{user.name} darf Benutzer hinzufügen."
end

Das funktioniert zwar wunderbar, aber bei genauerem hinsehen ist es bestenfalls "etwas unhübsch", um immer über die UserAccessRight-Entität zuzugreifen, wenn man eigentlich mit dem AccessRight selbst arbeiten möchte -- denn eigentlich soll ja weiterhin lediglich eine HABTM-Beziehung zwischen User und AccessRight bestehen, die aber so aufgelöst wird.

Dafür gibt's aber in Rails die folgende Lösung:


class User < ActiveRecord::Base
  has_many :user_access_rights
  has_many :access_right,
          :through => :user_access_right
end

class AccessRight < ActiveRecord::Base
  has_many :user_access_rights
  has_many :access_rights,
          :through => :user_access_rights,
          :unique => true
end

class UserAccessRight < ActiveRecord::Base
  belongs_to :user
  belongs_to :access_right
end

# Liste alle Rechte von Benutzer 1 auf:
user = User.find 1
user.rights.each do |acc|
  p acc.name
end

# darf der Benutzer also Benutzer hinzufügen?
if user.rights.find {|r| r.name == 'add_user'}
  p "Benutzer #{user.name} darf Benutzer hinzufügen."
end

So kann man also direkt, wie bei einer klassischen HABTM-Beziehung, auf die Rechte eines Benutzers zugreifen. Zusätzlich stehen aber auch unmittelbar die Meta-Informationen zu den einzelnen Rechten (wann und von wem verliehen) zur Verfügung.

Also ich finde das toll ;-)

Als Nächstes muss ich nur mal sehen, wie man halbwegs elegant an die Rechte herankommt, die einem Benutzer über seine Gruppenzugehörigkeiten verliehen werden. Zur Not tut's natürlich eine einfache Suchschleife über alle Gruppen und deren Rechte, oder die Rechte werden bei der Objektinitialisierung geladen, oder alles wird in einer nifty-tricky-magic Funktion gekapselt mit deren Innenleben sich lieber keiner auseinandersetzen sollte ;-)

TrackBack

TrackBack URL for this entry:
http://www.innoq.com/movabletype/mt-tb.cgi/2816

Comments (1)

Ich glaube, bei Black habe ich die Empfehlung gelesen, in solchen Fällen zunächst die "einfache" Lösung zu implementieren, die das ganze verständlich in Ruby löst, bevor man sie nachher durch kluges SQL löst. Genau danach riecht es hier auch ... Du weißt ja: "Premature optimization is the root of all evil" ;-)

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)