Jedna metoda do wielu modeli

W aplikacji mam wiele modeli powiązanych z modelem User w relacji jeden-do-wielu. Często potrzebuję “wyciągnąć” dane, których właścicielem jest aktualnie zalogowany użytkownik (current_user). Oczywiście mogę zastosować metodę find z parametrem conditions ale wygodniej by było stworzyć metodę find_own. Ale pisać tą metodę w każdym modelu, który jej wymaga?

Jak się do tego zabrać?

Nie do konca rozumiem, ale domyslam sie, ze mozesz pytac o dwie rzeczy, wiec dwie odpowiedzi… :wink:

Jezeli masz juz obiekt current_user, to dostep do danych masz poprzez metode o takiej samej nazwie jak nazwa asocjacji. Np.

@current_user.activities @current_user.projects
Jezeli chcialbys miec dostep do wszystkich powiazanych z uzytkownikiem danych, mozesz wykorzystac asocjacje polimorficzne.

Dziękuję za tą podpowiedź. Wygląda na to, że polimorficzne asosjacje rozwiążą mi wiele problemów.

Polimorfiki? Tutaj? Oszaleliście?

To odpowiedź czy diagnoza?

innymi słowy: mógłbyś rozwinąć tą myśl?

Popieram Tomasha, problem ktory przedstawil Tuptus nie da sie rozwiazac polimorficznymi asocjacjami… poniewaz owe asocjacje(niepolimorficzne) juz chyba istnieja ? To co chyba kolega chce zrobic to stworzyc pule finderow z ktorych moglby korzystac w wielu modelach. Nie wime tylko czy find_own to dobry przyklad poniewaz chyba tylko user bedzie mial taki finder?

Ogolnie to polecam lekture nastepujacych zagadnien :stuck_out_tongue:

  • named_scope z rails 2.x ktory swietnie sie nadaje do tworzenia finder’ow oraz ich chainowania, jesli znasz temat tworzenai finderow z witch_scope i uwazasz ze sam pomysl byl fajny ale jego wykonanie niezbyt wygodne w uzyciu to named_scope jest dla ciebie :slight_smile:
  • delegations (szczegolnie te w rails 2.2) ktore pozwalaja na oddelegowywanie metod z pod-obiektow np current_user.documents.attachemnts mozna oddelegowac na current_user.documents_attachments to tylko oczywiscie przyklad :slight_smile:
  • squirrel ktory jest bardzo fajna biblioteka do budowania warunkow sql w ruby, bardzo fajnie mozna budowac bloki warunkow i uwarunkowywac je if/elsif/unless bez brudzenia kodu, pozatym wspolgra z named_scope wiecej szczegolow tutaj http://github.com/thoughtbot/squirrel/tree/master

Natomiast jesli chodzi o zrefakotrowanie kilku finderow wspolnych dla wielu modeli to polecam metode include :slight_smile: (wiecej szczegolow znajdziesz szukajac informacji o mixin’ach w ruby) w teorii wyglada to mniejwiecej tak:

[code]class ModelA < ActiveRecord::Base
include BaseFinders
… kod modelu …
end

class ModelB < ActiveRecord::Base
include BaseFinders
… kod modelu …
end

class ModelB < ActiveRecord::Base
include BaseFinders
… kod modelu …
end

module BaseFinders
def self.included(klass)
klass.class_eval do
named_scope :named_scope_1, …
named_scope :named_scope_2, …
named_scope :named_scope_3, …
end
end
end[/code]
A w praktyce… no coz ja dopiero planuje zrefaktorowac swoja aplikacje w ten sposob. Wiec nie jestem w stanie podac zadnego praktycznego przykladu.

Oczywiscie dokladnie w ten sam sposob mozesz zrefaktorowac validacje oraz relacje.

I na koniec…Pamietajcie… nie popadajcie w paranoje refaktorujac kod :->

Jezeli dobrze zrozumialem pytanie, to sytuacja wyglada tak:
Mamy rozne elementy, ktore moga byc przypisane do uzytkownika, jednak te elementy posiadaja cechy wspolne, naleza do tej samej kategorii. I mozemy w jednym miejscu wylistowac wszystkie przypisane do danego uzytkownika. I tutaj zwiazki polimorficzne sa przeciez idealne.

Jednak jezeli elementy nie maja czesci wspolnych i chodzi tylko o to, ze metody wyszukujace elementy dla danego uzytkownika sa takie same dla roznych modeli, to oczywiscie rozwiazanie z named_scopes zamykanymi w modulach jest lepsze.

Pytanie tylko, o co autorowi chodzilo? Bo dla mnie nie jest to do konca jednoznaczne.

Nie rozumiem co dokładnie rozumiesz przez “części wspólne tych elementów”, chyba masz na myśli interfejs wspólny dla nich wszystkich ? Jeśli np. User ma swój “Workplace” albo “Toolbox” albo Dashboard który listuje wszystkie jego “rzeczy” i te rzeczy mogą być różnego typu ale ich lista jest podobna i podobnie się zachowują (np. wszystkie mają metodę name albo title którą wykorzystasz w liście) to zw. polimorficzne się jak najbardziej do tego nadają, ale potrzebujesz do tego osobnej tabeli która będzie przechowywać powiązania, załóżmy że użytkownik ma “Dashboard” który pokazuje listę jego “rzeczy”:

[code=ruby]class User
has_many :dashboard_items, :dependent => :destroy
end

class DashboardItem
belongs_to :user
belongs_to :dashboardable, :polymorphic => true
end

class Activity
has_many :dashboard_items, :as => :dashboardable, :dependent => :destroy
end

class Project
has_many :dashboard_items, :as => :dashboardable, :dependent => :destroy
end[/code]
tabelka dashboard_items:

id
user_id
dashboardable_id
dashboardable_type (string)

Jeśli chcesz dodać jakiś obiekt (Activity lub Project) do listy obiektów “dashboardable” użytkownika to piszesz, np:

project = Project.find(1)
project.dashboard_items.create(:user_id => current_user.id)

albo w drugą stronę:

project = Project.find(1)
current_user.dashboard_items.create(:dashboardable => project)

Do kolekcji obiektów danego użytkownika dostajesz się poprzez:

current_user.dashboard_items

a do konkretnego obiektu przez interfejs :dashboardable, np:

current_user.dashboard_items.first.dashboardable

np. powyższe wywołanie zwróci obiekt Project(id = 1)
czyli możesz np. napisać:

current_user.dashboard_items.first.dashboardable.title

i to zadziała dla obiektów każdego rodzaju pod warunkiem że obsługują .title

Widzę, że pytanie nie do końca zostało zrozumiane więc może bardziej obrazowo. Model User zawiera użytkowników m.in sprzedawców, klientów itd. Poza tym mam modele takie jak Message, Domain, Service… W każdym z tych modeli znajduje się pole user_id.
Załóżmy, że do serwisu zalogował się sprzedawca (jest w current_user) i chce wyświetlić listę wiadomości do edycji (ma prawo edytować tylko swoje wiadomości)… @messages=Message.find_own(:all)
Inny przypadek: klient chce zmienić parametry usługi z której korzysta … lista jego serwisów @services=Service.find_own(:all)
Mamy zatem dwa wspólne elementy we wszystkich przypadkach: użytkownik jest w current_user, wszystkie modele zawierają pole user_id. Poza tymi elementami modele są różne.

Czescia wspolna oczywiscie moze byc wspolny interfejs, ale nie tylko. Zobrazuje przykladem:
Uzytkownik ma w aplikacji kalendarz, w ktorym zarzadza roznymi zdarzeniami. Zdarzeniania posiadaja czas rozpoczecia, rodzaj przypomnienia (email, sms) itd. I to sa cechy wspolne.
Jednak zdarzeniami sa jednoczesnie wyjazdy (miejsce docelowe), spotkania z przyjaciolmi (lista znajomych, ktorzy maja sie pojawic; miejsce spotkania) oraz wazne telefony do wykonania (numer telefonu; cel rozmowy).
Konkretne zdarzenia zarzadzane sa przez osobne kontrolery, natomiast w kalendarzu wyswietlane sa w kontekscie tego samego typu.
I tutaj zastosowanie zwiazkow polimorficznych jest wrecz naturalne.

No, to w tym przypadku zdecydowanie odradzam zwiazki polimorficzne.

STI w rails: http://wiki.rubyonrails.org/rails/pages/singletableinheritance

[code]class User < ActiveRecorod::Base
… kod wpsolny dla kontaktow, sprzedawcow, klientow, firm trzecich …
end

class Contant < User
… kod tylko dla kontaktow …
end

class Client < User
… kod tylko dla klientow …
end

class Reseller < User
… kod dla sprzedawcow …
end[/code]
Moze tak ?

nie wie ktoś może przypadkiem gdzie to wywędrowało, lub widział coś podobnego ?

@Tuptus, żeby zrobić coś takiego jak podałeś w przykładach

[quote=Tuptus]@messages=Message.find_own(:all)
@services=Service.find_own(:all)[/quote]
to nie musisz nic kombinować, tylko zrób tak jak na początku napisał rafwic

@messages = current_user.messages @services = current_user.services

Jeżeli chciałeś zrobić tak

@messages = Message.find :all, :conditions => { :user_id => current_user.id  }

no to chyba nie masz w modelu User asocjacji has_many do poszczególnych modeli

class User has_many :services has_many :messages end
Albo ja nic nie rozumiem.

nie wie ktoś może przypadkiem gdzie to wywędrowało, lub widział coś podobnego ?[/quote]
Koncepcja jest u jak-zwykle-niezawodnego Fowlera:
http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html

A tu jest parę fajnych patentów, konkretnie fabryka:
http://www.theartofrails.com/hello-world/

Reszta – generalnie google “rails single table inheritance”