Podział zadań pomiędzy kontrolerem, a widokiem

0

Witam,
już od dłuższego czasu uczę się programować w PHP i staram się za każdym razem uczyć czegoś nowego i poszerzać swoją wiedzę. Do tej pory w większości przypadków korzystałem z dokumentacji php, czy też danego framework'a chcąc rozwiązać jakiś problem. Jednak niedawno postanowiłem wydzielić jeszcze trochę czasu wolnego na realizację jakichś tutoriali. I tak natrafiłem na Symfonycasts, gdzie nie ukrywam dowiedziałem się kilku nowych rzeczy na temat funkcjonalności, z których korzystałem wcześniej na podstawie dokumentacji Symfony, jednak jedna sprawa wciąż nie daje mi spokoju. Wcześniej programowałem trochę na czystym php (bez frameworków) i do frameworków przekonał mnie model MVC. Do tej pory pisząc w Symfony, czy to w innym frameworku starałem się maksymalnie jak się da przesunąć całą logikę do kontrolera, a w widoku wywoływać tylko wypisanie gotowych danych na ekran i tutaj wracając do Symfonycasts dowiedziałem się na początku, iż wszystkie funkcje i filtry twiga (domyślne, bundle, czy to własne ) są zawsze ładowane do pamięci niezależnie czy dany widok z nich korzysta, czy też nie. Następnie zostało zobrazowane, iż dane odnośnie powiązanych tabel w db można wyciągnąć bezpośrednio w twigu (zamiast tworzyć tablicę na np. komentarze do artykułu w kontrolerze i tam też wyciągnąć te dane a potem przesłać do widoku). Z tego co zrozumiałem z kursu to te dane są wtedy ładowane dopiero wtedy kiedy zostaną wywołane do wypisania (widzę w takim razie potencjał do pierwotnego ukrycia komentarzy i dopiero, gdy użytkownik kliknie przycisk 'pokaż komentarze' wywołanie linii kodu, która je wypisze na ekran, lecz o ile się nie mylę to samo można osiągnąć tworząc funkcję w kontrolerze i używając ajaxa) To samo się tyczy funkcji 'ago' w twigu ( można przecież napisać funkcję w php, która obliczy nam różnicę czasu i wrzuci do tablicy już gotowe dane) . Tutaj mam pytanie odnośnie tego jak używanie tych funkcji twigowych ma się więc do wydajności aplikacji i do ścieżki oddzielania logiki od widoku danego projektu, bo do tej pory myślałem, że właśnie lepiej jest napisać swoją funkcję w kontrolerze, czy też użyć bundla w kontrolerze niż bezpośrednio w widoku za pomocą twiga, a teraz po tym kursie nie jestem już tego taki pewien.

Za wszystkie odpowiedzi z góry dziękuję.

1

To, że powiązane dane z tabel w Twigu wyciągasz bardzo łatwo to kwestia jego współpracy z Doctrine i encjami (getery). Jednak każde zapytanie wyciągajace coś więcej (obiekt powiazany z innego obiektu) to dodatkowe zapytania do bazy (koszt czasowy).

Twig jest napisany w PHP, nie do końca rozumiem zdania o ponownym napisaniu funkcji w PHP.

Twig bardzo przyjemnie działa przy obróbce danych już przesłanych, ale unikaj dodatkowego wyciągania danych bezpośrednio w widoku. Właściwie to możesz z Symfony pozbyć się Twiga (jak i dołączyć go do innego frameworka) i obrabiać dane w czym tylko sobie zamarzysz.

0

To, że twig jest stworzony przez PHP, gdzieś już czytałem.

Twig bardzo przyjemnie działa przy obróbce danych już przesłanych, ale unikaj dodatkowego wyciągania danych bezpośrednio w widoku

Bardziej właśnie pytałem o miejsce wyciągania danych. Zawsze robiłem to w kontrolerze np.

$comments = $articleRep->findBy(['article' => $article ]);

Następnie wysyłałem tablicę 'comments' do widoku gdzie za pomocą twiga była ona tylko wypisywana na ekran. Stąd moja konsternacja i zaciekawienie tematem, gdy w Symfonycasts powiedziane zostało, że lepiej jest zastosować tzw. 'lazy loadind' w twigu ;) A skoro uznaję zasadę, że głupich pytań nie ma wolałem zapytać o to kogoś bardziej doświadczonego ode mnie niż zastanawiać się której opcji użyć przy każdym kolejnym projekcie stosując jedną bądź drugą opcję bez pewności jakie konsekwencje za tym idą. Dzięki wielkie za rozjaśnienie problemu.

0

widzę w takim razie potencjał do pierwotnego ukrycia komentarzy i dopiero, gdy użytkownik kliknie przycisk 'pokaż komentarze' wywołanie linii kodu, która je wypisze na ekran

Nie wiem czy dobrze zrozumiałem, więc wolę to wyjaśnić. Jeżeli nie użyjesz ajaxa, to widok jest przetworzony tak czy siak, więc te komentarze i tak będą pobrane od razu, nie ważne, czy ukryjesz tego diva, czy nie.

Używanie lazy loadingu ma jeden minus. Jak zrobisz w ten sposób podstronę pojedynczego posta, to jeszcze nie ma problemu, bo powiedzmy dociągniesz coś trzema zapytaniami. Problem się zaczyna, jak masz listę czegoś. Wyobraź sobie, że masz 30 komentarzy i w widoku robisz getAuthor i masz dodatkowe 30 zapytań.

0
Desu napisał(a):

widzę w takim razie potencjał do pierwotnego ukrycia komentarzy i dopiero, gdy użytkownik kliknie przycisk 'pokaż komentarze' wywołanie linii kodu, która je wypisze na ekran

Nie wiem czy dobrze zrozumiałem, więc wolę to wyjaśnić. Jeżeli nie użyjesz ajaxa, to widok jest przetworzony tak czy siak, więc te komentarze i tak będą pobrane od razu, nie ważne, czy ukryjesz tego diva, czy nie.

Nie chodziło mi o dosłowne ukrycie komentarzy (tzn. ukrycie diva z komentarzami ), a raczej po kliknięciu w ogóle dodania takowego diva. Co prawda jeszcze tego nie wypróbowałem, ale myślę że wtedy te dane zostaną załadowane przez twiga na stronę dopiero wtedy, gdy jsem dodam tego diva do widoku. No chyba że się mylę to prosiłbym o sprostowanie.

Używanie lazy loadingu ma jeden minus. Jak zrobisz w ten sposób podstronę pojedynczego posta, to jeszcze nie ma problemu, bo powiedzmy dociągniesz coś trzema zapytaniami. Problem się zaczyna, jak masz listę czegoś. Wyobraź sobie, że masz 30 komentarzy i w widoku robisz getAuthor i masz dodatkowe 30 zapytań.

No tak dla dociągania w ten sposób wielu pojedynczych rekordów z tabeli to traci jakikolwiek sens. Jedynie właśnie w przypadku tych komentarzy ( jak pobieramy całą powiązaną z danym postem historię komentarzy) lub jakieś pojedyncze rekordy (np. 'pokaż numer telefonu' w serwisie ogłoszeniowym) ma to jakikolwiek sens, choć i tutaj chyba bardziej skłaniam się ku wykorzystaniu po prostu ajaxa bez żadnych udziwnień.

0

No chyba że się mylę to prosiłbym o sprostowanie.

Generalnie są dwie ścieżki. Albo coś jest zaczytywane od razu przy pobraniu HTML'a z serwera i wtedy wykonują się wszystkie zapytania bez względu na to, czy coś jest widoczne, czy nie, albo doczytujesz to ajaxem.

Co do danych i lazy loadingu. Bardzo łatwo to nadużyć, zwłaszcza jak kod modyfikuje frontendowiec, który może nie wiedzieć, że jak wywoła trzy gettery, to pójdą 3 zapytania. I tak, to się dzieje. Zdarzało się, że optymalizowałem podstronę, na której było kilka tysięcy zapytań, bo ktoś sobie radośnie rozszerzył podstronę z opiniami o adresy i parę innych danych :P Co więcej był to framework, który jak wywołasz kilka razy getter, to on kilka razy to pobiera (WTF), a ktoś zrobił mniej więcej coś takiego:

row.getAddress().getStreet()
row.getAddress().getCity()
row.getAddress().getPostalCode()

To już 3 zapytania :D Bo jak wywołałeś getter, a nie dobrałeś się do pola magicznym getterem (row.address), to leciało zapytanie. Oczywiście opinie zostały dodane bez wiedzy backendowca, bo przeciez można zrobić getOpinions na encji user i wszystko śmiga, a paginacja była na zasadzie display: none i jak ktoś kliknął więcej, to po prostu robiło się display: block.

0

Generalnie są dwie ścieżki. Albo coś jest zaczytywane od razu przy pobraniu HTML'a z serwera i wtedy wykonują się wszystkie zapytania bez względu na to, czy coś jest widoczne, czy nie, albo doczytujesz to ajaxem.

To rozumiem myślałem, że to działa troszkę jak ajax, lecz wczoraj z ciekawości wypróbowałem, czy mój pomysł z dołączaniem diva za pomocą jsa wypali i widzę że nawet, gdy dany kod nie znajduje się de facto w początkowym htlmie strony tylko jest dołączany za pomocą kliknięcia w buttona (nie mówię tu display:none czy tego rodzaju trikach) to i tak funkcje twigowe zaciągają już na początku rekordy z bazy. Drugim moim spostrzeżeniem jest fakt, iż pomimo tego zapytanie to i tak wykonuje się dużo szybciej niż przy standardowym pobieraniu danych z db w kontrolerze i potem przesyłaniu ich do widoku (patrz załącznik). Skąd wynikają takie różnice?? Myślałem że różnicą pomiędzy tymi metodami jest głównie czas potrzebny na upakowanie wcześniej pobranych danych z db do tablicy i przesłania ich do tablicy w twigu, lecz te czasy na zdjęciu z załącznika (profiler doctrine) dotyczą chyba tylko czasu wykonania zapytania?

Cytując tutorial:

Here's what's going on. When Symfony queries for the Article, it only fetches the Article data: it does not automatically fetch the related Comments. And, for performance, that's great! We may not even need the comment data! But, as soon as we call getComments() and start using that, Doctrine makes a query in the background to go get the comment data.This is called "lazy loading": related data is not queried for until, and unless, we use it. To make this magic possible, Doctrine uses this PersistentCollection object. This is not something you need to think or worry about: this object looks and acts like an array.

Czyli ja to rozumiem tak: na początku Symfony tworzy w naszym obiekcie coś na wzór tablicy (tzw. PersistentCollection) rezerwując miejsce na dane dotyczące powiązanych w bazie danych tabel i nie używa tego dopóki my nie wykonamy w twigu metody np. $article->getComments();. Dobrze rozumiem?? Bo wydaje mi się, że już powoli zaczynam łapać jak to działa, ale nie wiem czy coś mi nie umyka. Przepraszam, że tak drążę temat, ale chciałbym mieć pewność że dobrze rozumiem zagadnienie.

1

W pierwszym zapytaniu szukasz po tekście, więc to już powoduje że są dłuższe czasy. Jeśli nałożyłbyś indeks na slug w bazie, to prawdopodobnie by trochę szybciej działało.

1 użytkowników online, w tym zalogowanych: 0, gości: 1