Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MVC] Wykonywanie określonej akcji, a elastyczność
Forum PHP.pl > Forum > PHP > Object-oriented programming
l0ud
Cześć. Jako, że tak naprawdę większość ostatnich moich projektów była w MVP, postanawiam to zmienić wink.gif

Zastanawiam się jak zrealizować dla przykładu rejestrację użytkownika. Chodzi mi o moment już odbierania formularza.
Podejście pierwsze, wstępne: wywołuje się akcja, która ma za zadanie rejestrację użytkownika. Waliduje ona określone pola pobrane z formularza i jeżeli wszystko jest OK, hashuje hasło i następnie wywołuje model użytkowników, do którego przekazuje te dane (no i są tam dodawane do bazy). Model nie zajmuje się walidacją wcale.

Problem tego podejścia jest taki, że chęć zarejestrowania użytkownika gdzieś indziej (z walidacją) wymaga kopiowania większości kodu akcji (bo nie można po prostu jej tak wywołać, gdyż robi ona niepożądane rzeczy typu wczytywanie widoku)... No i sama akcja jest rozpasła.

Podejście drugie: wydzielenie kodu walidacji użytkownika do oddzielnego kontrolera z akcją Register(), która zajmie się walidacją i w razie czego wypluje wyjątek walidacji który przechwycimy w akcji / miejscu, gdzie chcemy dodać użytkownika.
Wada: dodatkowy plik kontrolera, którego funkcjonalność wcześniej realizowała akcja.

Podejście trzecie: przeniesienie walidacji do modelu, po prostu wywołujemy metodę Register() w modelu, która zajmuje się wszystkim (ostatecznie wypluwa wyjątek walidacji/błędu bazy danych)
Wady: niemożność dodania do bazy danych niewalidujących się (a może być gdzieś w kodzie taka potrzeba), rozpasły model.

I teraz pytanie, które podejście będzie najlepsze, a może da się to zrobić logiczniej i prościej? smile.gif Nie potrzebuję konkretnych odpowiedzi, może być naprowadzenie na jakiś temat.

Pozdrawiam

mike
A dlaczego kontroler miąłby się zajmować walidacją? Wyrzuć walidacje do ... walidatora.
Ten sam zestaw komponentów walidujących mógłbyś użyć w kilku różnych miejscach. Nie powielisz kodu bo tylko wywołasz zewnętrzny komponent.
Crozin
Sprawdzenie poprawności danych musi odbyć się w modelu, w jakimś komponencie walidatora, bo tylko ta warstwa jest zdolna określić czy dane są w pełni poprawne. Co więcej nigdy nie powinno dojść do sytuacji, gdzie istnieje możliwość wprowadzenia nieprawidłowych danych. Co najwyżej może dojść do sytuacji gdzie istnieje kilka wariantów prawidłowych danych.
mike
Cytat(Crozin @ 17.08.2011, 15:10:46 ) *
Sprawdzenie poprawności danych musi odbyć się w modelu
Czy musi tutaj? Nie, nie musi.
Cytat(Crozin @ 17.08.2011, 15:10:46 ) *
(...) bo tylko ta warstwa jest zdolna określić czy dane są w pełni poprawne.
Nie tylko. Może się zdarzyć, że zależnie od konfiguracji dane mogą zmienić status z poprawnych na niepoprawne w danej chwili. Model to już za głęboko wtedy.
Cytat(Crozin @ 17.08.2011, 15:10:46 ) *
Co najwyżej może dojść do sytuacji gdzie istnieje kilka wariantów prawidłowych danych.
Walidacja może doprowadzić nie tylko do błędów ale i do ostrzeżeń.

Oczywistym krokiem jest wydzielenie sprawdzania poprawności do komponentów walidujących. ~l0ud przestanie mieć problem z DRY. Sam mechanizm walidacji może zostać uruchomiony przez zarówno przez kontroler jak i przez model.
~Crozin aplikacja odbiera z reguły różne dane. Model owszem może sprawdzać poprawność tego czym jest model data ale chyba nie oddelegujesz do modelu (nawet jeśli będzie to robił poprzez zewnętrzny komponent) sprawdzania poprawności adresu email lub faktu czy ktoś podał dwa takie same hasła w procesie rejestracji.
Model niech waliduje to co tyczy się modelu danych. Kontroler również może co nieco.

Kończąc. Nic nie musi. Bo musi to na Rusi tongue.gif
Crozin
@mike: Kwestia tego czy komponent sprawdzający poprawność emaila czy równość dwóch haseł nazwiesz częścią modelu czy nie. Nie upieram się że trzeba to traktować w ten sposób - w końcu nie wszystko w aplikacji musi należeć do którejś z trzech warstw architektury. Ot, ja traktuję to jako część modelu ponieważ te komponenty walidujące niejednokrotnie wykonują bezpośrednie operacje na danych przy czym nic nie stoi na przeszkodzie by przekazać im inny obiekt (już stricte "z modelu") przejmujący to zadanie - niejednokrotnie nawet jest to sensowniejsza opcja. Wtedy to już można śmiało powiedzieć, że taki komponent jest poza modelem.
smentek
@mike
Cytat
Sam mechanizm walidacji może zostać uruchomiony przez zarówno przez kontroler jak i przez model.


Ale chyba nie w aplikacji opartej na HTTP? Bo chyba piszesz o czymś co określa się jako 'aktywny model'? Ale może czegoś nie rozumiem podaj proszę jakiś przykład.

@Crozin
Cytat
Nie upieram się że trzeba to traktować w ten sposób - w końcu nie wszystko w aplikacji musi należeć do którejś z trzech warstw architektury


W aplikacje MVC "musi". Kod który nie należy do widoku ani kontrolera jest modelem. Zewnętrzne biblioteki z których korzysta aplikacja nie są częścią aplikacji w rozumieniu architektury systemu. Pojęciowo najbliżej im jednak do modelu. Jeżeli napiszemy sobie własny walidator i wywołamy go w naszej aplikacji w kontrolerze to walidator ten jest.... częścią modelu. Czyli masz walidację w modelu. Jeżeli przeniesiemy go do zewnętrznej biblioteki to znika z naszej aplikacji (przestaje być częścią jej modelu). Tym samym mamy identyczny kontroler ale tym razem walidacja jest już w kontrolerze smile.gif.

Także wszystko zależy od tego co uznajemy za bibliotekę a co za aplikację.

@mike
Cytat
Model owszem może sprawdzać poprawność tego czym jest model data ale chyba nie oddelegujesz do modelu (nawet jeśli będzie to robił poprzez zewnętrzny komponent) sprawdzania poprawności adresu email lub faktu czy ktoś podał dwa takie same hasła w procesie rejestracji.


Jeżeli walidując dwa hasła w procesie rejestracji utworzyłeś obiekt (obiekt walidatora którego klasę sam dorzuciłeś) i wywołałeś go w kontrolerze to zrobiłeś walidację w modelu. Niezależnie od tego czy obiekt ten sam zajmuje się walidacja czy pomaga sobie używając walidatorów z bibliotek zewnętrznych. Jeżeli w kontrolerze bezpośrednio wywołałeś walidatory biblioteki zewnętrznej (czyli nie tworzysz nowych klas) i tylko ewentualnie poustawiałeś ich stan, to jest to walidacja w kontrolerze.

Także nie można powiedzieć że walidacja jest w KOMPONENTACH. MVC to MVC a nie MVCK.
LSM
Może coś w ten deseń:
  1. // załóżmy, że chcesz uprościć kody klientów korzystające z rejestrowania nowego użytkownika:
  2. class GUIUserCreationController {
  3.  
  4. function registerCommonUser() {
  5. // ustawienie $data z $_POST
  6. $objSystemAccountFaccade->createUserAccount( $data );
  7. }
  8.  
  9. function registerCommonUserFromSOAPRequest() {
  10. // pobranie danych z odpowiedzi zew. systemu poprzez SOAP i ustawienie $data
  11. $objSystemAccountFaccade->createUserAccount( $data );
  12. }
  13.  
  14. function registerCommonUserFromFile() {
  15. // pobranie danych z pliku XML i utworzenie $data
  16. $objSystemAccountFaccade->createUnsafeUserAccountForTesting( $data );
  17. }
  18. }


Można skorzystać z wzorca Fasada czyli opakować to wszystko dodatkową klasą ( a jakże inaczej ;-) ):
  1. class SystemAccountFaccade {
  2. // [...]
  3.  
  4. function createUserAccount( $data = array() ) {
  5. // [...] tworzenie obiektów [...]
  6.  
  7. // TUser to klasa zawierająca operacje SQL oraz gromadząca dane o użytkowniku
  8. $objTUser->putData( array( 'passwd' => $data['mojeHaslo'], 'username'=> $data['Kazik'], 'photo'=> $data['photo'] ) );
  9.  
  10. // nie tworzę obiektu $objPasswordManager wew. TUser aby nie obciążać go niepotrzebną odpowiedzialnością, która już należy do PasswordManager
  11. $obTUser->passwd = $objPasswordManager->getPasswdHashedWith( 'sha256', $obTUser->passwd);
  12.  
  13. $objUserValidator->validateUsingDAO( $objTUser );
  14.  
  15. // accountManager realizuje dodatkowe tworzenie np. pustych pól w tabeli Company, ProductsTree, ustala uprawnienia do zasobów itp.
  16. $objSystemAccountManager->createNewUsing( $objTUser );
  17. $objMailer->sendMailUsing( $obTUser, 'registeringSuccess' );
  18. $strActionStatus = $objSystemAccountManager->getActionResultStatus();
  19. // [...]
  20. }
  21.  
  22. function createUnsafeUserAccountForTesting(){
  23. // [..] kopiujemy kod funkcji powyżej, ale usuwamy walidatora, wysyłanie email'a itp.
  24. // idąc dalej mozemy zajać się refaktoryzacją już samej klasy SystemAccountFaccade ale czy to ma sens ?
  25. }
  26. }


Zaprezentowane rozwiązanie daje nam narazie możliwość kopiowania i manipulacji kodem tworzenia nowego konta w jednej klasie używając wszystkich naszych klas typu AccountManager, Validator, ACLManager itd.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.