Viime blogi-kirjoituksessani käsittelimme liian isojen mallien jakamista moduuleihin, jotta ne säilyisivät selkeinä ja helposti luettavina. Tällä kertaa jatkamme samalla esimerkkikoodilla ja tutustumme lähemmin ActiveRecord-assosiaatioihin. Mikäli et vielä lukenut edellistä blogi-kirjoitusta, voit tehdä sen täällä.
Alla olen kirjoittanut auki BlogFinder-moduulin erään mahdollisen toteutuksen.
| # lib/blog_finder.rb module BlogFinder def find_users_published_posts(user) user.blog_posts.where(:published => true) end def find_users_unpublished_posts(user) user.blog_posts.where(:published => false) end def find_latest_published_posts(count) self.where(:published => true). limit(count). order(“updated_at DESC”) end def find_published_posts_with_category(category) self.where(:published => true, :category => category) end def advanced_search(options = {}) # end def quick_search(conditions) # end end |
Moni aloitteleva olio-ohjelmoija olisi voinut lisätä käyttäjiä koskevat blogien-hakumetodit User-luokkaan, mutta tällöin User-luokka olisi tiennyt liikaa blogien rakenteesta. Yllä olevassa toteutuksessa ei olla kuitenkaan noudatettu DRY-periaatetta, vaan samat hakuehdot toistuvat eri metodeissa.
Mikäli et vielä tunne scope–metodia läpikohtaisesti, suosittelen tutustumaan siihen heti. Scope–metodi lisää luokkakutsuja, jotka hakevat/rajaavat objekteja tietokannasta, mutta normaalien hakumetodien sijaan ne palauttavat ActiveRecord::Relation -objektin. ActiveRecord::Relation -objekteista tekee kiehtovia se, että ne toimivat samaan tapaan kuin find–metodin palauttamat joukot, mutta itse tietokantahaku suoritetaan vasta, kun yrität kutsua yhtä joukon instansseista. Tämän ansiosta voit ketjuttaa eri scope-kutsuja ilman ylimääräistä tietokantakutsua. Alla BlogFinder–moduuli toteutettuna scope-luokkametodeilla.
| # lib/blog_finder.rb module BlogFinder scope :published, where(:published => true) scope :unpublished, where(:published => false) scope :latest, lambda { |count| limit(count). order(“updated_at DESC”) } scope :in_category, lambda { |category| where(:category => category) } def find_users_published_posts(user) user.blog_posts.published end def find_users_unpublished_posts(user) user.blog_posts.unpublished end def find_latest_published_posts(count) self.published.latest(count) end def find_published_posts_with_category(category) self.published.in_category(category) end def advanced_search(options = {}) # end def quick_search(conditions) # end end |
Huomaa, että scope-metodi toimii myös assosiaatioille, ja rajoittuu vain tähän alijoukkoon. ActiveRecord-assosiaatiot palauttavat ensin ActiveRecord::Associations::AssociationProxy -objektin, joka on hyvin samanlainen objekti kuin ActiveRecord::Relation. Se mahdollistaa muun muassa luokkametodien kutsumisen alijoukolle eli tässä tapauksessa scope-metodin. Tälläkin kertaa tietokantaan päätyy ainoastaan yksi haku. (Rails 3.1:n myötä ActiveRecord::Associations on muuttunut paljon ja ActiveRecord::Associations::AssociationProxy on muun muassa nimetty uudelleen ActiveRecord::Associations::Association:ksi.)
Ensi kerralla jätämme mallit taaksemme ja siirrymme MVC:n C-osioon eli käsittelijään (controller).