Początkujący - tworzenie kolekcji

Witam!

zaczynam z railsami i mam zapewne trywialny problem ale nie mogę znaleźć podpowiedzi:

mam tabelę w bazie o nazwie arts zawierającą dane na temat dzieł sztuki, chciałbym stworzyć w modelu metodę która po podaniu parametrów selekcjonowałaby grupę przedmiotów spełniających podane kryteria i zwracała ich w postaci kolekcji. Zrobiłem to przy pomocy SQL-a, działa ale jest obrzydliwie prostackie:

def self.pokaz(author,status,typ,order)
case status
when ‘na_sprzedaz’: status = “rezerwacja is null and order_id is null”
when ‘sprzedane’: status = “order_id is not null”
when ‘rezerwacja’: status = “rezerwacja is not null”
else
status = “id is not null”
end
if !author.blank?
status = status + " and author_id=’"+author+"’"
end
if !typ.blank?
status = status + " and typ = ‘" +typ +"’"
end
if order.blank?
order = “tytul”
end
find(:all, :conditions => [status], :order => [order])
end

jak można to zrobić bardziej “po railsowemu”?

def self.search(options = {}) find(:all, :conditions => options end
W wywołaniu podajesz Art.search(:status => ‘rezerwacja’, :typ => ‘jakis typ’). Coś w tym stylu pewnie będzie ładniejsze. Polecam railscast gdzie Ryan przedstawia jak można używać metodę find z różnymi parametrami - tabele, nile, hashe.

wielkie dzięki za odpowiedź, ale jak mam okazję to chciałbym się jeszcze dowiedzieć jak mogę znaleźć przy pomocy tego rozwiązania przedmioty które mają np. cenę>1000 (cena jest atrybutem modelu Art)? I jeszcze tak trochę z innej strony - jak usuwać poszczególne elementy istniejącej kolekcji?

Odpowiedź:

Art.find(:all, :conditions => ["price > ?", wartosc_minimalna])

Jeśli zastosujemy tego typu składnie (tzn. z nawiasem kwadratowym i pytajnikiem), to “wartosc_minimalna” jest escapowane (nie trzeba się przejmować SQL injection).

To zależy czy chcesz je usunąć z kolekcji - wtedy delete, czy usunąć całkowicie (=zniszczyć, usunąć z bazy),
wtedy destroy. Dodatkowo jest wersja destroy!, która w przypadku niepowodzenia rzuca wyjątek.

Przykładowo - chcemy usunąć wszystkie obiekty klasy Art o jakimś tam parametrze “title”:

Art.find(:all,:conditions => ["title = ?", jakis_tam]).each{|e| e.destroy}

A jak wyszukać wszystkie obiekty które posiadają “potomstwo” (np. model author has_many :arts)?

Tutaj też masz kilka opcji - zależnie od tego, czy chcesz, żeby było to wydajne, czy prostsze :slight_smile:
Opcja najprostsza - wyciągasz z bazy wszystkie obiekty Author i odsiewasz te, które nie mają żadnych elementów Art:

Author.find(:all).reject!{|author| author.arts.empty? }

Druga opcja to zrobienie zapytania SQL-owego:

Author.find_by_sql("select * from (select a1.name name, count(arts.id) count from authors a1 left outer " + "join arts on a1.id = arts.author_id group by a1.id) as authors where count > 0")
No cóż - to rozwiązanie jest oczywiście najwydajniejsze, ale niezbyt miłe dla oka :slight_smile:

Ostatni rozwiązani - chyba najlepszy kompromis, polega na dodaniu w tabeli authors pola arts_count, a w modelu przy asocjacji włączeniu zliczania powiązanych obiektów (:counter_cache => true). Wtedy zapytanie będzie wyglądało następująco:

Author.find(:all, :conditions => ["arts_count > 0"])

Jedyna wada tego rozwiązania jest taka, że sami musimy pamiętać o zaktualizowaniu licznika, kiedy przenosimy np. jakiś produkt artystyczny z jednego autora do innego.