Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [Symfony2][Symfony]Odchudzanie kontrolerów
athabus
post 14.10.2015, 13:20:30
Post #1





Grupa: Zarejestrowani
Postów: 898
Pomógł: 48
Dołączył: 2.11.2005
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Kolejne moje pytanie z serii "jak to poprawnie zorganizować w Symfony". Otóż napotykam na problem, że przy bardziej rozbudowanej aplikacji moje kontrolery zaczynają bardzo "puchnąć". Przykład z dzisiaj - interakcje miedzy różnymi Entity.

Załóżmy, że buduję generator ćwiczeń na siłowni, którego celem jest wygenerowanie planu na dany dzień, z określonymi parametrami.

Moje Entity wyglądają tak:

- ćwiczenie - to bazowe ćwiczenia typu pompki, podciąganie na drążku etc z np. przypisanymi trudnościami w skali 1-5
- zadanie - zadanie to wykonanie pewnej ilości ćwiczeń - np. 5 pompek
- seria - grupa zadań do wykonania (czyli np. 5x pompki, 10x podciąganie) itd.
- plan - ilość serii do wykonania

I teraz załóżmy, że piszę generator, który ma wylosować taki plan, gdzie zawodnik nazbiera łącznie np 100punktów za wykonane ćwiczenia. I tu się rodzi pytanie gdzie umieścić algorytm losowania takiego planu - czyli np. mógłby on wyglądać tak:
- wybierz losowo 3 ćwiczenia -> dobierze ilość powtórzeń tak aby seria miała 25 punktów -> stwórz 4 takie serie i zapisz je jako plan na dzisiaj

Można to zrobić w kontrolerze, ale wtedy taki kontroller strasznie puchnie. W demo_app do symfony właśnie tak to robią, ale tam mają bardzo prostą aplikację, która ogranicza się do CRUD, więc logiki tam praktycznie nie ma.

Można by zrobić to w modelu, ale w teorii z tego co zrozumiałem, to model nie ma odpowiadać za logikę tylko odczyt/zapis danych, a w tym przypadku taki entity Plan musiałby odczytywać/zapisywać/analizować dane z innych Entity, czyli zaczyna się robić spaghetti code.

Gdzieś widziałem, że ktoś postulował przerzucanie wszystkiego do services i wstrzykiwanie do nich wszystkich potrzebnych rzeczy typu ObjectManager itd. Czyli przykładowo robimy services "planCwiczen" i w nim znajduje się cała logika wyboru ćwiczeń, zapisywania wszystkiego w bazie itd. a w kotrolerze mamy tylko cos w stylu $this->get('planCwiczen')->createWorkout(). Pytanie tylko czy Services były tworzone z myślą do przechowywania całej logiki aplikacji?

Jak wy sobie z tym radzicie? Gdzie trzymacie tą część kodu odpowiedzialną za algorytm działania?
Go to the top of the page
+Quote Post
Crozin
post 14.10.2015, 13:49:09
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Na początek wyjaśnijmy kilka podstawowych rzeczy:
1. Model to nie jest wybieranie/zapisywanie danych z/do bazy danych! Model to w kontekście MVC cała warstwa aplikacji odpowiedzialna za przetwarzanie danych. Czyli będziesz miał tam zarówno wyciąganie danych z bazy (DAO) jak i ich przetwarzanie (generator planu ćwiczeń).
2. Jedna klasa/obiekt nie powinna zajmować się wieloma rzeczami. Jeżeli jeden obiekt odpowiedzialny jest za wyciąganie danych nie powinien on już skupiać się na ich przetwarzaniu.

Wracając do pytania. Powinieneś utworzyć sobie właśnie taką klasę jak GeneratorPlanuĆwiczeń, która może mieć metodę wygeneruj(), która na podstawie jakiś tam parametrów zwróci obiekt PlanĆwiczeń, który to zaś będzie zawierać w sobie Serie, Zadania i Ćwiczenia. Jeżeli generator potrzebuje do swojego działania jakiś zewnętrznych zależności powinny zostać one przekazane w konstruktorze (stąd jak najbardziej można skorzystać z Symfonowego kontenera w celu tworzenia takiego obiektu). Generator to generator, a nie "zapisywator", więc sam zapis obiektu PlanĆwiczeń powinien być zrealizowany przez inny obiekt. W efekcie kontroler mógłby wyglądać tak:
  1. class WorkoutController
  2. {
  3. /** @var SomeWorkoutGenerator */
  4. private $workoutGenerator;
  5.  
  6. /** @var WorkoutStore */
  7. private $workoutStore;
  8.  
  9. public function __construct(...) { ... }
  10.  
  11. public function generateAction(Request $request)
  12. {
  13. $workoutPlan = $this->workoutPlanGenerator->generate($request->query->get('pointsLimit', 25));
  14.  
  15. // jeżeli potrzebujesz możesz jeszcze dostosować plan, np.:
  16. $workoutPlan->setOwner($this->getUser());
  17.  
  18. $this->workoutStore->store($workoutPlan);
  19.  
  20. return new Response('Nowy plan ćwiczeń został wygenerowany i zapisany.');
  21. }
  22. }
Go to the top of the page
+Quote Post
athabus
post 14.10.2015, 14:13:47
Post #3





Grupa: Zarejestrowani
Postów: 898
Pomógł: 48
Dołączył: 2.11.2005
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Ad1 - oczywiście skrót myślowy, mówiąc model miałem na myśli klasy Entity/Repository.

Ad2 - czyli jednak też w przypadku Symfony jesteś za użyciem Services. Wychodzi na to, że trochę źle zrozumiałem ich znaczenie w Symfony. Bardziej traktowałem je jako dostarczycieli dodatkowych usług typu obsługa kont użytkowników, mailer etc, ale nie jako nośnik całej logiki aplikacji. Chyba jednak muszę je szerzej zrozumieć.

Czyli ogólnie wg. Ciebie poprawne rozwiązanie to w przypadku Symfony stworzenie service typu "workautGenerator", wstrzyknięcie zależności typu EntityManager, zwrócenie obiektów do kontrolera, a sam kontroler decyduje o zapisie, czy innym użyciu zwróconego rezultatu. Tak to już ma dla mnie więcej sensu, bo właśnie bolało mnie to, że w zasadzie Service stawał się sam w sobie aplikacja odpowiedzialną za wszystko, a kontroller sprowadzał się do wywołania service. W takim kodzie jak wrzuciłeś wyżej jest to sensowniej rozwiązane i funkcje poszczególnych "klocków" się nie dublują.
Go to the top of the page
+Quote Post
thek
post 14.10.2015, 20:37:47
Post #4





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Zgadzam się z Crozinem. Osobiście Entity same w sobie jest u mnie jedynie odpowiedzialne za operacje CRUD, na które nałożone jest Repository jako dostarczyciel i obrabiarka danych, ale jeśli ma być coś z tym sensownego robione, co wykracza poza daną encję oraz jej repozytorium i staje się bardziej rozbudowane, wydzielam do osobnych usług za to odpowiedzialnych. I tu właśnie świetnie sprawdzają się Services, które konieczne dla startu rzeczy typu entity manager czy walidatory dostają ładnie do konstruktora z użyciem DI. a odwołanie do nich w kontenerze jest banalne. Wszystkie ograniczenia, parametry, warunki i tak dalej może właśnie kontroler przekazać do serwisu,


--------------------
Najpierw był manual... Jeśli tam nie zawarto słów mądrości to zapytaj wszechwiedzącego Google zadając mu własciwe pytania. A jeśli i on milczy to Twój problem nie istnieje :D
Go to the top of the page
+Quote Post
athabus
post 15.10.2015, 06:51:59
Post #5





Grupa: Zarejestrowani
Postów: 898
Pomógł: 48
Dołączył: 2.11.2005
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Zdecydowanie źle zrozumiałem ideę Services. Dokumentacja i przykładowa aplikacja trochę tutaj wprowadzają w błąd (albo ja źle odebrałem), bo nie piszą o tym aby "algorytmy" przenosić do Services tylko bardziej rozumiałem to jako zewnętrze usługi typu mailer wałkowany w dokumentacji czy np. parser bbcode itp. Jakoś nie miałem świadomości, że można tam wrzucać core samej aplikacji, ale faktycznie ma to sens, bo można w ten sposób uzyskać wiele kodu, który może przydać się w innych miejscach - np. gdybym w tej "dummy app", którą tu opisywałem chciał np. zrobić kiedyś ćwiczenie tygodnia, to wystarczy odpowiednia refektorować kod w Service. a nie znowu dłubać w samym kontrolerze.

Wczoraj spróbowałem tego podejścia w aplikacji nad którą pracuję, a która ma podobny problem jak ten powyżej tylko trochę bardziej rozbudowany i na pewno kod wygląda o wiele lepiej niż kontroler z bzyliardem funkcji.
Go to the top of the page
+Quote Post
thek
post 16.10.2015, 18:40:52
Post #6





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Pamiętaj, że Services możesz ładnie odseparować funkcjonalnie, zgodnie z regułami SOLID, a by całość działała w miarę sprawnie i bez zbędnego żonglowania nimi w kontrolerze, Dependency Injection Container może wstrzykiwać zależności. To naprawdę nic niezwykłego, że Symfony w DIC wstrzykiwuje jeden serwis do innego.


--------------------
Najpierw był manual... Jeśli tam nie zawarto słów mądrości to zapytaj wszechwiedzącego Google zadając mu własciwe pytania. A jeśli i on milczy to Twój problem nie istnieje :D
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 18.04.2024 - 08:42