About

This page contains a single entry from the blog posted on January 20, 2009 10:22 PM.

The previous post in this blog was Dokumentenarchivierung und OCR unter Linux.

The next post in this blog is Die Grenzen von Scopes in ActiveRecord.

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

Powered by
Movable Type 3.31

« Dokumentenarchivierung und OCR unter Linux | Main | Die Grenzen von Scopes in ActiveRecord »

@shop.products.published.for_category(@category).since(3.years.ago).bought_by(@me)

Wenn es etwas gibt, was uns im aktuellen Projekt die Haut gerettet hat, dann ist es vermutlich named_scope. Gerade in dem Moment, wo Vladimir und ich eigentlich entschieden hatten, dass wie die Grenze des mit ActiveRecord elegant lösbaren überschritten hatten, sprang es mir ins Gesicht.

Hintergrund: Hat man ein komplexes Datenmodell mit vielen n-ären Abhängigkeiten und einer entsprechend kombinatorischen Anzahl an interessanten Abfragen, so stößt man mit find_by_x_and_y(:conditions => '...', :joins => '...') doch schnell an seine Grenze. Beispiel:

Category.all(
  :select     => 'categories.*, COUNT(DISTINCT categories_products.product_id) AS product_count,
                  AVG(product_reports.popularity) AS category_popularity',
  :joins      => 'INNER JOIN categories_products
                    ON categories_products.category_id=categories.id
                  INNER JOIN products
                    ON categories_products.product_id=products.id
                  INNER JOIN product_groups
                    ON products.group_id=product_group.id
                  INNER JOIN product_reports
                    ON product_reports.product_tribe_id=product_groups.id
                  INNER JOIN shop_products
                    ON shop_products.product_id=products.id',
  :group      => 'category_id',
  :conditions => 'shop_products.workflow_state=\'published\' AND categories.shop_id=42',
  :order      => 'category_popularity')

Wer findet heraus, was diese Query liefert?

Die Lösung für dieses Problem lautet, wie schon gesagt, Named Scopes. Diese erlauben es, Bedingungen bei Abfragen beliebig zu kombinieren. Ein Beispiel:

Category.for_shop(42).published.by_popularity

das liefert das gleiche Ergebnis wie die erste Query und ist gleichzeitig um längen flexibler. Was steckt dahinter? Dieses:

class Category
  named_scope :published,
    :conditions => "shop_products.workflow_state='published'"

  named_scope :by_popularity,
    :order  => 'AVG(product_reports.popularity)',
    :group  => 'category_id',
    :joins  => 'INNER JOIN categories_products
                  ON categories_products.category_id=categories.id
                INNER JOIN products
                  ON categories_products.product_id=products.id
                INNER JOIN product_groups
                  ON products.group_id=product_group.id
                INNER JOIN product_reports
                  ON product_reports.product_tribe_id=product_groups.id
                INNER JOIN shop_products
                  ON shop_products.product_id=products.id'

  named_scope :for_shop, lambda {|shop_id|
    {:conditions => ['categories.shop_id=?', shop_id]}
  }
end

Ein Named Scope ist also eine Art abgespeckter Beziehungsdefinition (has_many, has_one etc. definieren intern auch Scopes), die zur Generierung einer komplexeren SQL-Query herangezogen werden kann. Somit lässt sich die Komplexität der Queries wieder schön kapseln und außer an der Stelle ihrer Definition hat man nirgends in der Anwendung mit SQL zu tun, sondern mit gut lesbaren Konstrukten wie @shop.products.published.for_current_shop.since(3.years.ago).bought_by(@current_user). Außerdem spart man sich das händische Erstellen vieler spezialisierter find-Methoden!

Lang lebe ActiveRecord ;)

*) Anmerkung: Beim Projekt handelt es sich nicht, wie vllt angedeutet, um ein Shopsystem. Der Code wurde quasi anonymisiert.

TrackBack

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

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.)