Symfony 2 od podstaw
 9788324662418, 9788324634989, 8324634983, 8324662413

Citation preview

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Redaktor prowadzący: Ewelina Burska Projekt okładki: Jan Paluch Materiały graficzne na okładce zostały wykorzystane za zgodą Shutterstock.

Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie?symfo2_ebook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Przykłady omówione w książce są dostępne pod adresem: ftp://ftp.helion.pl/przyklady/symfo2.zip

ISBN: 978-83-246-6241-8

Copyright © Helion 2012 Printed in Poland. • Poleć książkę na Facebook.com

• Księgarnia internetowa

• Kup w wersji papierowej

• Lubię to! » Nasza społeczność

• Oceń książkę

Spis treści Podziękowania .............................................................................. 13

Część I Rozdział 1.

Tworzenie prostych stron WWW .................................... 15 Uruchomienie przykładowego projektu ........................................... 17 Dystrybucja with vendors — około 6 MB ....................................................................... 17 Dystrybucja without vendors — około 200 kB ............................................................. 17 Przykład 1.1. Aplikacja przykładowa ............................................................................ 18 ROZWIĄZANIE ..................................................................................................... 18 Podsumowanie .............................................................................................................. 22

Rozdział 2.

Hello, world! .................................................................................. 25 Przestrzenie nazw .......................................................................................................... 25 Pakiet ............................................................................................................................. 26 Kontroler i akcja ............................................................................................................ 27 Widok ............................................................................................................................ 28 Przykład 2.1. Hello, world! ........................................................................................... 28 ROZWIĄZANIE ..................................................................................................... 28 Zmodyfikowane pliki .................................................................................................... 39 Środowiska pracy .......................................................................................................... 40 Tworzenie i usuwanie pakietów .................................................................................... 42 Użycie przestrzeni nazewniczych .................................................................................. 42 Cechy Symfony 2 .......................................................................................................... 44 Formaty konfiguracji ............................................................................................... 44 Uruchomienie gotowego przykładu ............................................................................... 46

Rozdział 3.

Dołączanie zewnętrznych zasobów ................................................. 47 Przykład 3.1. Pusta Dolinka .......................................................................................... 49 ROZWIĄZANIE ..................................................................................................... 49 Przykład 3.2. Dolina Pięciu Stawów Polskich ............................................................... 53 ROZWIĄZANIE ..................................................................................................... 53

Rozdział 4.

Szablon witryny ............................................................................. 57 Przykład 4.1. Dwa kabele .............................................................................................. 60 ROZWIĄZANIE ..................................................................................................... 61

Rozdział 5.

Hiperłącza i struktura aplikacji ...................................................... 65 Tworzenie i usuwanie akcji ........................................................................................... 65 Tworzenie i usuwanie kontrolerów ............................................................................... 67

4

Symfony 2 od podstaw Tworzenie i usuwanie pakietów .................................................................................... 67 Definiowanie adresów URL akcji ................................................................................. 68 Przykład 5.1. Fraszki ..................................................................................................... 69 ROZWIĄZANIE ..................................................................................................... 69 Przykład 5.2. Zabytki Lublina ....................................................................................... 72 ROZWIĄZANIE ..................................................................................................... 74 Przykład 5.3. Piosenki dla dzieci ................................................................................... 77 ROZWIĄZANIE ..................................................................................................... 78

Rozdział 6.

Błędy 404 ..................................................................................... 83 Strony błędów w Symfony 2 ......................................................................................... 84 Przykład 6.1. Gady ........................................................................................................ 86 ROZWIĄZANIE ..................................................................................................... 86 Nadpisywanie widoków dowolnych pakietów .............................................................. 91 Programowe generowanie błędów 404 oraz 500 ........................................................... 92

Rozdział 7.

Publikowanie projektu na serwerze hostingowym ........................... 93 Przykład 7.1. Gady — wersja lokalna z własną domeną ............................................... 93 ROZWIĄZANIE ..................................................................................................... 94 Przykład 7.2. Gady — wersja z serwera firmy NetArt .................................................. 95 ROZWIĄZANIE ..................................................................................................... 95 Przykład 7.3. Gady — wersja z serwera firmy Light Hosting ....................................... 97 ROZWIĄZANIE ..................................................................................................... 97

Rozdział 8.

Podsumowanie części I ............................................................... 101 Dystrybucje Symfony 2 ............................................................................................... 101 Przykładowa aplikacja ACME demo .......................................................................... 101 Pierwszy samodzielnie wykonany projekt ................................................................... 102 Zewnętrzne zasoby ...................................................................................................... 103 Szablon witryny .......................................................................................................... 103 Podstawy routingu ....................................................................................................... 104 Błędy 404 .................................................................................................................... 104 Publikowanie projektu ................................................................................................. 105 Przykład 8.1. Przygotowanie pakietu symfony2-customized-v1.zip (bez przykładu src/Acme) ......................................................................................... 106 ROZWIĄZANIE ................................................................................................... 106

Część II

Widoki ....................................................................... 109

Rozdział 9.

Twig ........................................................................................... 111 Logiczne nazwy widoków ........................................................................................... 111 Nadpisywanie widoków z folderu vendor ................................................................... 113 Nazwy widoków akcji ................................................................................................. 114 Przykład 9.1. Nazwy logiczne widoków, adnotacja @Template() i metoda render() ....... 116 ROZWIĄZANIE ................................................................................................... 116 Składnia widoków Twig .............................................................................................. 119 Wyłączanie interpretacji w szablonie .......................................................................... 120 Przykład 9.2. Wyłączanie interpretacji fragmentu szablonu ........................................ 121 ROZWIĄZANIE ................................................................................................... 122 Podwójne rozszerzenie .html.twig ............................................................................... 123 Modyfikacja nagłówka Content-Type przy użyciu parametru _format ................. 124 Modyfikacja nagłówka Content-Type metodą set() .............................................. 124 Przykład 9.3. Modyfikacja nagłówka Content-Type ................................................... 125 ROZWIĄZANIE ................................................................................................... 125

Spis treści

5

Rozdział 10. Zmienne, wyrażenia i operatory Twig ........................................... 129 Przekazywanie zmiennych do widoku ........................................................................ 129 Przykład 10.1. Data i godzina ...................................................................................... 130 ROZWIĄZANIE ................................................................................................... 131 Zabezpieczanie zmiennych .......................................................................................... 132 Przykład 10.2. Zabezpieczanie zmiennych .................................................................. 134 ROZWIĄZANIE ................................................................................................... 135 Przekazywanie do widoku tablic ................................................................................. 138 Przekazywanie do widoku obiektów ........................................................................... 139 Wyrażenia Twig .......................................................................................................... 139 Operatory Twig ........................................................................................................... 141 Definiowanie zmiennych wewnątrz widoku ................................................................ 144 Zmienne globalne ........................................................................................................ 145

Rozdział 11. Instrukcje sterujące for oraz if ..................................................... 147 Instrukcja for ............................................................................................................... 147 Instrukcja if ................................................................................................................. 150 Przykład 11.1. Korona ziemi ....................................................................................... 151 ROZWIĄZANIE ................................................................................................... 152 Przykład 11.2. Dzieła literatury światowej .................................................................. 155 ROZWIĄZANIE ................................................................................................... 155 Przykład 11.3. Tabliczka mnożenia ............................................................................. 157 ROZWIĄZANIE ................................................................................................... 157 Przykład 11.4. Tabela potęg ........................................................................................ 161 ROZWIĄZANIE ................................................................................................... 161 Przykład 11.5. Bezpieczna paleta kolorów .................................................................. 163 ROZWIĄZANIE ................................................................................................... 164

Rozdział 12. Znaczniki, filtry i funkcje ............................................................. 169 Znaczniki Twig ........................................................................................................... 169 Znaczniki for oraz if .............................................................................................. 171 Znaczniki macro, from i import ............................................................................ 171 Znacznik filter ....................................................................................................... 172 Znacznik set .......................................................................................................... 173 Znacznik extends ................................................................................................... 173 Znacznik block ...................................................................................................... 175 Znaczniki extends i block oraz dziedziczenie ....................................................... 175 Znacznik use ......................................................................................................... 178 Znacznik include ................................................................................................... 179 Znacznik spaceless ................................................................................................ 179 Znacznik autoescape ............................................................................................. 180 Znacznik raw ......................................................................................................... 180 Znacznik flush ....................................................................................................... 180 Znacznik do ........................................................................................................... 180 Znacznik render ..................................................................................................... 181 Filtry ............................................................................................................................ 181 Funkcje ........................................................................................................................ 184 Przykład 12.1. Piosenki dziecięce ............................................................................... 185 ROZWIĄZANIE ................................................................................................... 186

Rozdział 13. Trójstopniowy podział widoków .................................................... 195 Przykład 13.1. Opowiadania Edgara Allana Poe ......................................................... 197 ROZWIĄZANIE ................................................................................................... 198

Rozdział 14. Podsumowanie części II .............................................................. 205

6

Symfony 2 od podstaw

Część III Dostosowywanie Symfony 2 ........................................ 207 Rozdział 15. Dodawanie nowych pakietów ....................................................... 209 Lista pakietów zawartych w Symfony ......................................................................... 209 Zawartość folderu vendor/ ........................................................................................... 210 Pobieranie pakietów do folderu vendor/ .......................................................................... 211 Dołączanie pakietów do kodu ..................................................................................... 212 Przykład 15.1. Przygotowanie dystrybucji symfony2-customized-v2 zawierającej pakiet DoctrineFixturesBundle ............................................................ 212 ROZWIĄZANIE ................................................................................................... 213

Rozdział 16. Podsumowanie części III ............................................................. 217

Część IV Praca z bazą danych ................................................... 219 Rozdział 17. Pierwszy projekt wykorzystujący bazę danych .............................. 221 Przykład 17.1. Imiona ................................................................................................. 221 ROZWIĄZANIE ................................................................................................... 222

Rozdział 18. ORM Doctrine 2 .......................................................................... 233 Tworzenie i usuwanie bazy danych ............................................................................. 233 Doctrine 2.1 ................................................................................................................. 234 Tworzenie tabel w bazie danych ................................................................................. 235 Struktura klas dostępu do bazy danych ....................................................................... 236 Dodawanie nowych właściwości do istniejącej klasy ................................................. 237 Typy danych ................................................................................................................ 238 Operowanie klasami dostępu do bazy danych ............................................................. 240 Klasy Entity i EntityManager ................................................................................ 240 Stan obiektu Entity ................................................................................................ 241 Tworzenie nowych rekordów ................................................................................ 242 Usuwanie rekordów .............................................................................................. 243 Pobieranie wszystkich rekordów z bazy ................................................................ 243 Przykład 18.1. Rzeki ................................................................................................... 243 ROZWIĄZANIE ................................................................................................... 244

Rozdział 19. Dostosowywanie klas dostępu do bazy danych ............................. 251 Klasy Entity oraz Repository ....................................................................................... 251 Podstawowe metody klas Repository .......................................................................... 252 Metoda find() ........................................................................................................ 252 Metoda findAll() ................................................................................................... 253 Metoda findBy() .................................................................................................... 253 Metoda findOneBy() ............................................................................................. 254 Metoda findByX() ................................................................................................. 254 Metoda findOneByX() .......................................................................................... 255 Nadpisywanie metod klasy Entity ............................................................................... 255 Metoda __toString() klasy Entity .......................................................................... 255 Metoda fromArray () klasy Entity ......................................................................... 256 Nadpisywanie metod klasy Repository ....................................................................... 256 Przykład 19.1. Tatry .................................................................................................... 257 ROZWIĄZANIE ................................................................................................... 257

Rozdział 20. Podsumowanie części IV ............................................................. 265

Spis treści

7

Część V

Zachowania Doctrine ................................................. 267

Rozdział 21. Instalacja i konfiguracja rozszerzeń DoctrineExtensions ................ 269 Przykład 21.1. Przygotowanie dystrybucji symfony2-customized-v3 zawierającej pakiet StofDoctrineExtensionsBundle .................................................. 270 ROZWIĄZANIE ................................................................................................... 270

Rozdział 22. Zachowanie sluggable ................................................................. 275 Identyfikatory slug ...................................................................................................... 275 Automatyczne generowanie identyfikatorów slug w Symfony 2 ................................ 276 Przykład 22.1. Wyrazy — test zachowania sluggable ................................................. 277 ROZWIĄZANIE ................................................................................................... 277 Parametry adnotacji konfigurujących wartości slug .................................................... 280

Rozdział 23. Zachowanie timestampable ......................................................... 281 Przykład 23.1. Wyrazy — test zachowania timestampable ......................................... 282 ROZWIĄZANIE ................................................................................................... 282

Rozdział 24. Zachowanie translatable ............................................................. 283 Wstawianie tłumaczeń do bazy danych ....................................................................... 284 Odczytywanie tłumaczeń ............................................................................................ 286 Przykład 24.1. Kolory — test zachowania timestampable .......................................... 286 ROZWIĄZANIE ................................................................................................... 287

Rozdział 25. Podsumowanie części V .............................................................. 293

Część VI Szczegółowe dane rekordu .......................................... 295 Rozdział 26. Akcja show ................................................................................. 297 Adresy URL zawierające zmienne .............................................................................. 297 Konwersja wejściowa ............................................................................................ 298 Konwersja wyjściowa ........................................................................................... 298 Wyszukiwanie pojedynczego rekordu na podstawie klucza głównego ....................... 298 Wyświetlanie właściwości rekordu ............................................................................. 299 Przykład 26.1. Piosenki wojskowe .............................................................................. 299 ROZWIĄZANIE ................................................................................................... 300

Rozdział 27. Identyfikacja rekordu na podstawie wartości slug ........................ 307 Przykład 27.1. Piosenki wojskowe — użycie identyfikatorów slug ............................ 308 ROZWIĄZANIE ................................................................................................... 308

Rozdział 28. Generowanie menu na podstawie zawartości bazy danych ............ 311 Przykład 28.1. Treny ................................................................................................... 311 ROZWIĄZANIE ................................................................................................... 312

Rozdział 29. Udostępnianie plików binarnych ................................................... 319 Przykład 29.1. Download — pliki zapisane w bazie danych ....................................... 320 ROZWIĄZANIE ................................................................................................... 320 Przykład 29.2. Download — pliki pobierane z folderu ............................................... 325 ROZWIĄZANIE ................................................................................................... 325

Rozdział 30. Podsumowanie części VI ............................................................. 327

8

Symfony 2 od podstaw

Część VII Relacje ...................................................................... 329 Rozdział 31. Relacje 1:1 ................................................................................. 331 Klucze obce o wartości NULL .................................................................................... 332 Użycie relacji 1:1 w Symfony 2 .................................................................................. 332 Operowanie rekordami powiązanymi relacją .............................................................. 334 Tworzenie rekordów ............................................................................................. 334 Rekord zależny ...................................................................................................... 335 Przykład 31.1. Dane użytkowników ............................................................................ 335 ROZWIĄZANIE ................................................................................................... 335 Akcje referencyjne SQL .............................................................................................. 338 Programowe akcje referencyjne Doctrine 2.1 .............................................................. 339 Parametr cascade ................................................................................................... 339 Parametr orphanRemoval ...................................................................................... 340 Relacje jednokierunkowe i dwukierunkowe ................................................................ 340 Synchronizacja obiektów z bazą danych ........................................................................ 342

Rozdział 32. Relacje 1:n (jeden do wielu) ........................................................ 345 Klucze obce o wartości NULL .................................................................................... 346 Użycie relacji 1:n w Symfony 2 .................................................................................. 346 Właściciel relacji 1:n ................................................................................................... 349 Operowanie rekordami powiązanymi relacją .............................................................. 349 Tworzenie rekordów ............................................................................................. 349 Rekordy zależne .................................................................................................... 350 Rekord nadrzędny ................................................................................................. 351 Synchronizacja relacji ................................................................................................. 351 Akcje referencyjne ...................................................................................................... 352 Akcje SQL-owe ..................................................................................................... 352 Akcje Doctrine ...................................................................................................... 352 Przykład 32.1. Kontynent i państwa ............................................................................ 353 ROZWIĄZANIE ................................................................................................... 353 Porządkowanie rekordów ............................................................................................ 357

Rozdział 33. Relacje n:m (wiele do wielu) ........................................................ 359 Użycie relacji n:m w Symfony 2 ................................................................................. 360 Właściciel relacji n:m .................................................................................................. 361 Tabela łącząca relacji n:m ........................................................................................... 362 Operowanie rekordami powiązanymi relacją .............................................................. 362 Tworzenie rekordów ............................................................................................. 362 Rekordy zależne .................................................................................................... 363 Synchronizacja relacji ........................................................................................... 364 Usuwanie powiązania relacyjnego ........................................................................ 364 Akcje referencyjne SQL .............................................................................................. 365 Akcje SQL-owe ..................................................................................................... 365 Przykład 33.1. Filmy i aktorzy .................................................................................... 365 ROZWIĄZANIE ................................................................................................... 365 Porządkowanie rekordów ............................................................................................ 370

Rozdział 34. Relacje, akcje index i show oraz widoki częściowe ....................... 373 Przykład 34.1. Kontynenty/Państwa — akcje show i widoki częściowe ..................... 375 Przykład 34.2. Filmy/Aktorzy — akcje show i widoki częściowe .............................. 376 Przykład 34.3. Powieści Agaty Christie ...................................................................... 376 ROZWIĄZANIE ................................................................................................... 377

Spis treści

9

Rozdział 35. Podsumowanie części VII ............................................................ 385

Część VIII Panele CRUD i zabezpieczanie dostępu do aplikacji .... 387 Rozdział 36. Generowanie paneli administracyjnych CRUD ............................... 389 Adresy URL akcji CRUD ..................................................................................... 391 Ponowne generowanie paneli CRUD .......................................................................... 394 Panele CRUD a relacje ................................................................................................ 394 Przykład 36.1. Imiona — panel CRUD ....................................................................... 394 ROZWIĄZANIE ................................................................................................... 395 Przykład 36.2. Panel CRUD i relacja 1:1 .................................................................... 396 ROZWIĄZANIE ................................................................................................... 396 Przykład 36.3. Panel CRUD i relacja 1:n .................................................................... 399 ROZWIĄZANIE ................................................................................................... 399 Przykład 36.4. Panel CRUD i relacja n:m ...................................................................... 401 ROZWIĄZANIE ................................................................................................... 401

Rozdział 37. Instalacja pakietu FOSUserBundle ............................................... 403 Przykład 37.1. Przygotowanie dystrybucji symfony2-customized-v4 zawierającej pakiet FOSUserBundle ......................................................................... 403 ROZWIĄZANIE ................................................................................................... 403 Tworzenie kont i nadawanie uprawnień ...................................................................... 408 Tworzenie kont ..................................................................................................... 409 Aktywacja i deaktywacja konta ............................................................................. 409 Nadawanie i usuwanie uprawnień administracyjnych ........................................... 409 Przykład 37.2. Sprawdzenie działania dystrybucji symfony2-customized-v4 ............. 410 ROZWIĄZANIE ................................................................................................... 410

Rozdział 38. Aplikacja dostępna wyłącznie dla zdefiniowanych użytkowników ...... 415 Uprawnienia dostępu ................................................................................................... 415 Role użytkowników ..................................................................................................... 416 Nadawanie, usuwanie i sprawdzanie uprawnień użytkownikom ................................. 417 Przykład 38.1. Korona ziemi ....................................................................................... 419 ROZWIĄZANIE ................................................................................................... 420 Hierarchia ról .............................................................................................................. 427

Rozdział 39. Aplikacja dostępna publicznie w trybie do odczytu ........................ 429 Przykład 39.1. Korona ziemi — podział na frontend oraz backend ............................ 429 ROZWIĄZANIE ................................................................................................... 430 Przekierowania ............................................................................................................ 432 Osadzanie formularza do logowania na stronie głównej ............................................. 434 Przykład 39.2. Korona ziemi — osadzenie formularza do logowania w pliku base.html.twig ........................................................................................................... 435 ROZWIĄZANIE ................................................................................................... 435

Rozdział 40. Rejestracja użytkowników i odzyskiwanie hasła ........................... 439 Przykład 40.1. Kontynenty/państwa — frontend i backend ........................................ 439 ROZWIĄZANIE ................................................................................................... 439 Przykład 40.2. Kontynenty/państwa — rejestracja użytkowników ............................. 442 ROZWIĄZANIE ................................................................................................... 442 Przykład 40.3. Kontynenty/państwa — odzyskiwanie hasła ....................................... 444 ROZWIĄZANIE ................................................................................................... 444

Rozdział 41. Podsumowanie części VIII ........................................................... 447

10

Symfony 2 od podstaw

Część IX Panele administracyjne Sonata ................................... 449 Rozdział 42. Instalacja pakietów Sonata .............................................................. 451 Przykład 42.1. Przygotowanie dystrybucji symfony2-customized-v5 zawierającej pakiet SonataAdminBundle .................................................................. 451 ROZWIĄZANIE ................................................................................................... 452 Krok 1. Wypakuj dystrybucję Symfony 2.0.X without vendors ............................ 452 Krok 2. Zmodyfikuj plik deps ............................................................................... 452 Krok 3. Pobierz pakiety ......................................................................................... 453 Krok 4. Usuń foldery .git ...................................................................................... 453 Krok 5. Zarejestruj przestrzenie nazw ................................................................... 453 Krok 6. Zarejestruj pakiety .................................................................................... 454 Krok 7. Zmodyfikuj konfigurację projektu ........................................................... 454 Krok 8. Zmodyfikuj zabezpieczenia projektu ....................................................... 455 Krok 9. Utwórz pakiet Application/Sonata/UserBundle ....................................... 457 Krok 10. Zmodyfikuj reguły routingu ................................................................... 457 Krok 11. Zainstaluj style CSS oraz ikony ............................................................. 458 Krok 12. Skompresuj otrzymaną dystrybucję ....................................................... 458 Przykład 42.2. Sprawdź działanie dystrybucji symfony2-customized-v5 ................... 458 ROZWIĄZANIE ................................................................................................... 459 Krok 1. Wypakuj dystrybucję i skonfiguruj bazę danych ..................................... 459 Krok 2. Utwórz tabele w bazie danych ................................................................. 459 Krok 3. Utwórz konto administratora .................................................................... 459 Krok 4. Sprawdź wygląd panelu administracyjnego ............................................. 459

Rozdział 43. Użycie paneli administracyjnych Sonata do własnych tabel ............. 461 Przykład 43.1. Miasta .................................................................................................. 461 ROZWIĄZANIE ................................................................................................... 462 Krok 1. Wypakuj dystrybucję i skonfiguruj bazę danych ..................................... 462 Krok 2. Utwórz pakiet My/Frontend ..................................................................... 462 Krok 3. Utwórz klasę CityAdmin .......................................................................... 462 Krok 4. Włącz panel administracyjny do zarządzania rekordami City .................. 463 Krok 5. Przygotuj plik zawierający tłumaczenia ................................................... 464 Krok 6. Sprawdź wygląd panelu administracyjnego do edycji miast .................... 464

Rozdział 44. Podsumowanie części IX ............................................................. 467 Przykład 44.1. Przygotowanie dystrybucji symfony2-customized-v6 zawierającej omówione pakiety ................................................................................ 467 Przykład 44.2. Rzeki: aplikacja z panelem Sonata ...................................................... 468 ROZWIĄZANIE ................................................................................................... 468 Krok 1. Połącz przykład 18. z dystrybucją symfony2-customized-v6.zip ............. 468 Krok 2. Wykonaj panel Sonata ............................................................................. 469 Przykład 44.3. Kontynenty: aplikacja z panelem Sonata ............................................. 469 ROZWIĄZANIE ................................................................................................... 469 Przykład 44.4. Filmy: aplikacja z panelem Sonata ...................................................... 470 Przykład 44.5. Powieści Agaty Christie: aplikacja z panelem Sonata ......................... 470

Dodatki ..................................................................... 471 Dodatek A

Instalacja oprogramowania .......................................................... 473 1. XAMPP ................................................................................................................... 473 2. Modyfikacja konfiguracji PHP ................................................................................ 475 3. Modyfikacja pakietu PEAR ..................................................................................... 476 4. Uaktualnienie biblioteki PEAR ............................................................................... 476

Spis treści

11 5. Code Sniffer ............................................................................................................ 477 6. phpDocumentor ....................................................................................................... 477 7. PHPUnit .................................................................................................................. 477 8. Cygwin .................................................................................................................... 478 9. Ścieżki dostępu ........................................................................................................ 480 10. GraphViz ............................................................................................................... 482 11. NetBeans ............................................................................................................... 482

Skorowidz ................................................................................... 483

12

Symfony 2 od podstaw

.

Podziękowania Serdecznie dziękuję:  Fabienowi Potencierowi i wszystkim programistom biorącym udział w rozwoju

Symfony 2 za wysiłek włożony w opracowanie, udokumentowanie i bezpłatne udostępnienie wspaniałego oprogramowania;  pracownikom Wydawnictwa Helion, szczególnie Pani Redaktor Ewelinie Burskiej,

za cierpliwość i profesjonalizm;  studentom Katolickiego Uniwersytetu Lubelskiego im. Jana Pawła II, którzy

w latach 2011 – 2012 uczestniczyli w prowadzonych przeze mnie zajęciach dotyczących Symfony 2;  uczestnikom organizowanych przeze mnie szkoleń: Wojciechowi Cupie,

Mateuszowi Draganowi, Tomaszowi Fudali, Andrzejowi Krynieckiemu, Wojciechowi Matyśkiewiczowi, Piotrowi Piskozubowi, Albertowi Rybackiemu, Ireneuszowi Sachowi, Pawłowi Zalechowi, Michałowi Zboinie oraz Pawłowi Zdyblowi, za opinie na temat materiału, który posłużył do opracowania niniejszego podręcznika;  moim najbliższym za wsparcie i mobilizację.

Włodzimierz Gajda Lublin, 20 maja 2012 r.

14

Symfony 2 od podstaw

Część I

Tworzenie prostych stron WWW

16

Część I ♦ Tworzenie prostych stron WWW

Rozdział 1. ♦ Uruchomienie przykładowego projektu

17

Rozdział 1.

Uruchomienie przykładowego projektu Oprogramowanie Symfony 2 jest dostępne w postaci dwóch różnych dystrybucji:  Symfony Standard with vendors, nazwa pliku: Symfony_Standard_Vendors_2.0.x.zip;  Symfony Standard without vendors, nazwa pliku: Symfony_Standard_2.0.x.zip.

Dystrybucje te różnią się wyłącznie zawartością folderu vendor/.

Dystrybucja with vendors — około 6 MB Dystrybucja with vendors zawiera w folderze vendor/ komplet niezbędnych bibliotek. Plik ten zajmuje ok. 6 MB i jest to gotowa aplikacja Symfony 2, którą możemy uruchomić. Dystrybucja Symfony 2.0 with vendors jest odpowiednikiem dystrybucji sandbox z Symfony 1.4.

Dystrybucja without vendors — około 200 kB Dystrybucja without vendors nie zawiera folderu vendor/. Zanim uruchomimy projekt oparty na tej dystrybucji, musimy doinstalować wszystkie niezbędne pakiety. W początkowym okresie nauki będziemy wykorzystywali dystrybucję with vendors. Dystrybucja without vendors stanie się przydatna, gdy zaczniemy wykorzystywać dodatkowe pakiety.

Część I ♦ Tworzenie prostych stron WWW

18

Po odwiedzeniu strony http://symfony.com/download pobierz najnowszą wersję dystrybucji with vendors. W chwili pisania tego tekstu był to plik Symfony_Standard_Vendors_ 2.0.10.zip.

Przykład 1.1. Aplikacja przykładowa Zanim przejdziemy do nauki Symfony 2, upewnijmy się, że stanowisko pracy jest poprawnie skonfigurowane. W tym celu wystarczy uruchomić aplikację przykładową, która jest zawarta wewnątrz dystrybucji Symfony 2.

ROZWIĄZANIE Krok 1. Utwórz nowy projekt Symfony 2 W folderze przeznaczonym na aplikacje WWW1 utwórz folder Symfony/. Do folderu tego wypakuj zawartość archiwum Symfony_Standard_Vendors_2.0.X.zip2. Po wykonaniu tej operacji zawartość folderu Symfony/ powinna być taka jak na rysunku 1.1. Rysunek 1.1. Katalogi i pliki utworzone po wypakowaniu archiwum Symfony_Standard_ Vendors_2.0.X.zip

Poszczególne foldery widoczne na rysunku 1.1 zawierają:  app/ — pliki konfiguracyjne aplikacji;  bin/ — polecenia wsadowe dotyczące pakietów dodatkowych (polecenie bin/vendors wykorzystamy m.in. do instalacji pakietu doctrine-fixtures

ułatwiającego wypełnianie bazy danych rekordami);  src/ — kod źródłowy aplikacji;  vendor/ — pakiety dodatkowe, m.in.: 1

Procedura instalacji oprogramowania jest przedstawiona w dodatku A. Jeśli przygotowałeś stanowisko pracy zgodnie z podanym opisem, to tym folderem jest C:\xampp\htdocs\.

2

W chwili pisania książki najnowszą dostępną wersją była wersja Symfony_Standard_Vendors_2.0.10.zip. Wszystkie podane przykłady zostały wykonane w wersji 2.0.10. Symfony 2 jest obecnie rozwijane i w chwili wydania książki dostępne będą z pewnością nowe wersje.

Rozdział 1. ♦ Uruchomienie przykładowego projektu

19

 doctrine — oprogramowanie ORM zapewniające dostęp do bazy danych;  twig — system szablonów;  swiftmailer — biblioteka ułatwiająca wysyłanie poczty elektronicznej;  web/ — folder zawierający główny kontroler aplikacji — skrypt app.php

(ang. front controller), style CSS, pliki graficzne oraz pliki JavaScript (jest to jedyny folder, który jest dostępny publicznie za pomocą protokołu HTTP). Statyczne pliki zawarte w folderze web/ (m.in style .css, skrypty .js oraz pliki graficzne .jpg i .gif) są w oryginalnej dokumentacji określane wspólnym terminem assets.

W folderze web/ znajdują się trzy pliki PHP:  web/app.php,  web/app_dev.php,  web/config.php.

Skrypt app.php uruchamia aplikację w środowisku produkcyjnym3, a skrypt app_dev.php — w środowisku deweloperskim. Skrypt config.php sprawdza natomiast, czy zainstalowane oprogramowanie spełnia minimalne wymagania stawiane przez Symfony 2. Podane trzy skrypty są dostępne pod adresami: http://localhost/Symfony/web/config.php http://localhost/Symfony/web/app.php http://localhost/Symfony/web/app_dev.php

Krok 2. Sprawdź wygląd strony web/app_dev.php Uruchom przeglądarkę i odwiedź w niej adres: http://localhost/Symfony/web/app_dev.php Powinieneś ujrzeć stronę przedstawioną na rysunku 1.2. Zanim przejdziesz do kolejnego rozdziału, musisz poprawnie wyświetlić stronę z rysunku 1.2.

Błędy, które mogą wystąpić Podczas wyświetlania strony z rysunku 1.2 mogą wystąpić następujące błędy:  Zainstalowane oprogramowanie nie spełnia wymagań Symfony 2.  Podjęto próbę dostępu do skryptu hello-world/web/app_dev.php poprzez sieć. 3

Więcej o środowiskach w kolejnym rozdziale.

Część I ♦ Tworzenie prostych stron WWW

20

Rysunek 1.2. Strona o adresie web/app_dev.php  Folder hello-world/app/cache/ zawiera nieaktualną wersję plików i wymaga

odświeżenia.  Akcelerator4 zapamiętał niepoprawne ścieżki do plików.

Błąd: zbyt stare oprogramowanie Jeśli po odwiedzeniu adresu: http://localhost/Symfony/web/app_dev.php ujrzysz pustą stronę WWW, może to świadczyć o tym, że zainstalowana jest zbyt stara wersja PHP. W celu upewnienia się, że zainstalowane oprogramowanie jest odpowiednie, odwiedź adres: http://localhost/Symfony/web/config.php Powinieneś ujrzeć stronę widoczną na rysunku 1.3.

4

Błąd ten występuje w systemie Windows 7, gdy w PHP zainstalowany jest akcelerator APC.

Rozdział 1. ♦ Uruchomienie przykładowego projektu

21

Rysunek 1.3. Strona sprawdzająca, czy zainstalowane oprogramowanie jest odpowiednie

Oczywiście należy usunąć wszelkie błędy zgłaszane przez skrypt widoczny na rysunku 1.3.

Błąd: próba zdalnego dostępu do app_dev.php Jeśli stronę z rysunku 1.2 zechcesz odwiedzić z innego komputera, podając adres serwera, na którym utworzyłeś folder Symfony/, np.: http://192.168.0.5/Symfony/web/app_dev.php http://moj.serwer.example.net/Symfony/web/app_dev.php ujrzysz wówczas komunikat o zakazie dostępu: You are not allowed to access this file...

W celu ominięcia powyższego zabezpieczenia usuń z pliku Symfony/web/app_dev. php kod: if (!in_array(@$_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1', ))) { header('HTTP/1.0 403 Forbidden'); exit('You are not allowed to access this file. Check '.basename(__FILE__).' for ´more information.'); }

Część I ♦ Tworzenie prostych stron WWW

22

Błąd: nieaktualne pliki w folderze app/cache/ Po odwiedzeniu adresu: http://localhost/Symfony/web/app_dev.php w folderze Symfony/app/cache/ tworzone są foldery i pliki zawierające przetworzone informacje o konfiguracji projektu. W niektórych przypadkach (np. wtedy, gdy przeniesiesz projekt do innego folderu) zawartość folderu Symfony/app/cache/ może być nieaktualna. W celu wyczyszczenia pamięci podręcznej projektu usuń wszystkie pliki i foldery znajdujące się w folderze Symfony/app/cache/.

Błąd: akcelerator nie odświeża ścieżek Jeśli korzystasz z akceleratora APC, to po przeniesieniu projektu do innego folderu możesz napotkać problemy polegające na odwoływaniu się przez projekt do nieistniejących plików oraz folderów. Problem ten wyeliminujesz, restartując serwer Apache. Jeśli w adresie: http://localhost/hello-world/web/app_dev.php spróbujesz pominąć nazwę pliku app_dev.php: http://localhost/hello-world/web/ ujrzysz wówczas stronę z tekstem: Oops! An Error Occurred The server returned a "404 Not Found".

Nie świadczy to o żadnym błędzie. Wszystko przebiega poprawnie. Adres: http://localhost/hello-world/web/app_dev.php uruchamia aplikację w środowisku deweloperskim, w którym strona główna wygląda tak jak na rysunku 1.2. Adres: http://localhost/hello-world/web/ uruchamia natomiast aplikację w środowisku produkcyjnym. Ponieważ w tym środowisku aplikacja jest pusta, tj. nie zawiera żadnej strony WWW, wyświetlany jest komunikat o nieodnalezionej stronie WWW: 404 Not Found.

Podsumowanie Uruchamiając przykładową aplikację, poznaliśmy rolę, jaką odgrywają pliki i foldery widoczne na rysunku 1.4.

Rozdział 1. ♦ Uruchomienie przykładowego projektu

23

Rysunek 1.4. Pliki i foldery poznane podczas uruchamiania przykładowej aplikacji

Symfony 2 jest rozpowszechniane w postaci dwóch dystrybucji:  with vendors,  without vendors.

Dystrybucja without vendors nie zawiera folderu vendor/, w którym znajdują się rozmaite pakiety konieczne do uruchomienia aplikacji. Dlatego naukę rozpoczynamy od dystrybucji with vendors. Pamiętaj o roli, jaką odgrywają:  folder app/cache/  oraz skrypty:

web/config.php web/app.php web/app_dev.php W folderze app/cache/ zapisywana jest zawartość pamięci podręcznej aplikacji. W celu odświeżenia pamięci podręcznej możesz usunąć zawartość tego folderu. Podane trzy skrypty PHP mają adresy: http://localhost/Symfony/web/config.php http://localhost/Symfony/web/app.php http://localhost/Symfony/web/app_dev.php Pierwszy z nich — config.php — sprawdza, czy zainstalowane oprogramowanie spełnia warunki Symfony 2. Skrypt app_dev.php wyświetla przykładową aplikację widoczną na rysunku 1.2. Skrypt app.php powoduje wyświetlenie informacji o błędzie.

24

Część I ♦ Tworzenie prostych stron WWW

Rozdział 2.

Hello, world! Pierwsza aplikacja, którą wykonamy, ma Cię zapoznać z procesem tworzenia i uruchamiania projektu oraz ze strukturą aplikacji. Oprogramowanie Symfony 2 jest zaimplementowane obiektowo z wykorzystaniem przestrzeni nazw (ang. namespace). Aplikacja tworzona w Symfony 2 jest podzielona na:  pakiety (ang. bundle),  kontrolery (ang. controller),  akcje (ang. action),  widoki (ang. view).

Przestrzenie nazw Przestrzenie nazw umożliwiają stosowanie wieloczłonowych nazw klas. Dzięki temu nazwy klas zawartych w aplikacji tworzą strukturę drzewa. Na przykład w Symfony 2 występują klasy o nazwach: Symfony\Component\Finder\Finder Symfony\Component\DomCrawler\Crawler Symfony\Component\ClassLoader\UniversalClassLoader

Przestrzenie nazw rozwiązują dwa istotne problemy dotyczące nazewnictwa klas w dużych projektach:  Gwarantują niezależność nazw klas tworzonych przez grupy programistów.  Umożliwiają stosowanie skróconych nazw.

Dzięki temu wewnątrz własnej aplikacji możemy utworzyć klasę o nazwie Lorem, nie przejmując się tym, czy w innym miejscu aplikacji istnieje klasa o identycznej nazwie. Przestrzenie nazw tworzymy, umieszczając klasy w osobnych folderach i dołączając deklarację namespace.

Część I ♦ Tworzenie prostych stron WWW

26

W celu utworzenia w aplikacji klasy o pełnej nazwie: Lorem\Ipsum\Dolor\Sit.php

należy utworzyć foldery Lorem, Ipsum i Dolor oraz umieścić w nich plik Sit.php: Lorem\Ipsum\Dolor\Sit.php

Plik Sit.php rozpoczynamy od deklaracji: namespace Lorem\Ipsum\Dolor; class Sit { }

Pełną nazwą klasy jest: Lorem\Ipsum\Dolor\Sit

W celu utworzenia obiektu klasy Sit możesz wykonać instrukcję: new Lorem\Ipsum\Dolor\Sit();

W celu skrócenia powyższego zapisu w dowolnym pliku aplikacji możesz dodać instrukcję: use Lorem\Ipsum\Dolor\Sit;

dzięki czemu tworzenie obiektu klasy Sit przyjmie krótszą formę: new Sit();

Pakiet Pakiety są niezależnymi fragmentami aplikacji. Każdy z pakietów może być wykorzystywany w wielu różnych aplikacjach. Cały pakiet jest umieszczony wewnątrz jednego folderu i może zawierać kontrolery, akcje, widoki, pliki konfiguracyjne, klasy pomocnicze oraz zasoby takie jak style CSS, obrazy czy skrypty JavaScript. Pojedynczy pakiet może stanowić małą cząstkę aplikacji, np.:  KnpMarkdownBundle — zestaw klas do interpretacji plików w języku

Markdown;  KnpPaginatorBundle — zestaw klas ułatwiających wykonywanie stronicowania (odpowiednik klas Pager z Symfony 1.4 oraz Zend

Framework). Przykładami pakietów nieco większych są:  DoctrineFixturesBundle — obsługa plików fixtures.yml, które ułatwiają

wypełnianie bazy danych;

Rozdział 2. ♦ Hello, world!

27

 DoctrineExtensionsBundle — obsługa zachowań sluggable, timestampable

itd. dla obiektów Doctrine;  DoctrineMigrationsBundle — obsługa migracji baz danych.

Dużymi fragmentami aplikacji są pakiety:  SonataAdminBundle — panel administracyjny CRUD;  FOSUserBundle — administracja kontami użytkowników (m.in. rejestracja

z potwierdzaniem e-mailem, resetowanie i zmiana hasła);  SonataPageBundle — system CMS;  FOSCommentBundle — obsługa wielowątkowych komentarzy.

Wspólną cechą wszystkich pakietów jest ich niezależność od konkretnej aplikacji. Celem tworzenia pakietów jest ułatwienie ponownego wykorzystania fragmentu projektu. Nazewnictwo pakietów jest dwuczłonowe. Pierwszy człon nazwy pakietu możemy traktować jako nazwę autora pakietu, a drugi — jako nazwę samego pakietu. Pakiet Symfony 2 jest odpowiednikiem wtyczki z Symfony 1.4.

Kontroler i akcja W Symfony 2 kontroler odpowiada za przetworzenie żądania HTTP i wygenerowanie odpowiedzi. Wprawdzie kontrolerem może być zarówno metoda klasy, jak i zwykła funkcja, jednak w przykładach omawianych w dalszej części podręcznika w roli kontrolera będziemy stosowali wyłącznie metody klasy. Na przykład w projekcie omawianym w bieżącym rozdziale wystąpi klasa Default ´Controller, a w niej metoda: public function indexAction() { }

Dla ułatwienia będę stosował konwencję nazewniczą z frameworków Symfony 1.4 oraz Zend Framework. Kontrolerem będę nazywał klasę (np. DefaultController), zaś akcją — metodę (np. indexAction()). Unikniemy wówczas dwuznaczności. Dokumentacja Symfony 2 stosuje termin „kontroler” w odniesieniu do zarówno klasy (np. DefaultController), jak i samej metody (np. indexAction()).

Część I ♦ Tworzenie prostych stron WWW

28

Widok Widoki to pliki, które zawierają kod HTML oraz specjalne instrukcje umieszczające w kodzie HTML wartości zmiennych. W Symfony 2 domyślnym językiem przetwarzania widoków jest Twig. Pliki widoków mają podwójne rozszerzenie .html.twig.

Przykład 2.1. Hello, world! Wykorzystując oprogramowanie Symfony 2, wykonaj aplikację, która będzie prezentowała stronę WWW z tekstem Hello, world!. Zadanie rozwiąż w taki sposób, by adres strony kończył się napisem /hello-world.html. Jeśli podczas wykonywania przykładu ujrzysz białą stronę, pamiętaj, że pierwszym krokiem do usunięcia powstałego błędu jest wyczyszczenie pamięci podręcznej i odświeżenie strony. W tym celu najlepiej wyczyścić zawartość folderu app/cache/. Komenda: php app/console cache:clear

często zawodzi. Drugim krokiem, który stosuję w przypadku błędnego działania akceleratora APC, jest restart serwera Apache. Jeśli i to nie pomaga, sprawdzam, jakie informacje zwraca skrypt web/config.php.

ROZWIĄZANIE Krok 1. Utwórz nowy projekt Symfony 2 W folderze przeznaczonym na aplikacje WWW1 utwórz folder hello-world/. Do folderu tego wypakuj zawartość archiwum Symfony_Standard_Vendors_2.0.X.zip. Po wykonaniu tej operacji zawartość folderu hello-world/ powinna być taka jak na rysunku 2.1.

Krok 2. Usuń pakiet demo Plik Symfony_Standard_Vendors_2.0.X.zip zawiera pakiet demonstracyjny, który nie jest konieczny do tworzenia nowych aplikacji. W celu usunięcia pakietu demo:  Usuń folder hello-world/src/Acme/.  W pliku hello-world/app/AppKernel.php usuń wpis: $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();

 W pliku hello-world/app/config/routing_dev.php usuń wpisy przedstawione

na listingu 2.1.  Usuń folder hello-world/web/bundles/acmedemo/. 1

Procedura instalacji oprogramowania jest przedstawiona w dodatku A. Jeśli przygotowałeś stanowisko pracy zgodnie z podanym opisem, to tym folderem jest C:\xampp\htdocs\.

Rozdział 2. ♦ Hello, world!

29

Rysunek 2.1. Foldery i pliki projektu Hello, World!

Listing 2.1. Reguły, które należy usunąć z pliku hello-world/app/config/routing_dev.php _welcome: pattern: / defaults: { _controller: AcmeDemoBundle:Welcome:index } _demo_secured: resource: "@AcmeDemoBundle/Controller/SecuredController.php" type: annotation _demo: resource: "@AcmeDemoBundle/Controller/DemoController.php" type: annotation prefix: /demo

Krok 3. Tworzenie pakietu Pakiety tworzymy, uruchamiając w wierszu poleceń komendę: php app/console generate:bundle

Powyższa komenda będzie działała poprawnie wyłącznie wtedy, gdy program php.exe jest dostępny w ścieżkach poszukiwań. Należy także pamiętać, że php.exe uruchamiane wsadowo może mieć inną konfigurację niż PHP uruchamiane protokołem HTTP. Do sprawdzenia poprawności instalacji PHP w wierszu poleceń służy skrypt app/check.php, który uruchamiamy poleceniem: php app/check.php

Procedura instalacji PHP w wierszu poleceń jest szczegółowo opisana w dodatku A.

Krok 3.1. Utwórz nowy pakiet My/HelloworldBundle Uruchom wiersz poleceń i komendami cd przejdź do folderu hello-world/. Następnie wydaj komendę: php app/console generate:bundle

która wygeneruje nowy pakiet (ang. bundle) wewnątrz projektu. Po wydaniu komendy w odpowiedzi na monit: Bundle namespace:

Część I ♦ Tworzenie prostych stron WWW

30

wprowadź nazwę pakietu: My/HelloworldBundle

Nazwa pakietu jest dwuczłonowa. Pierwszy człon może być traktowany jako oznaczenie autora pakietu. Nazwa: My/HelloworldBundle

spowoduje utworzenie w folderze src/ folderów: hello-world/ src/ My/ HelloworldBundle/

Cały kod tworzonego pakietu zostanie umieszczony wewnątrz folderu: hello-world/src/My/HelloworldBundle/. Nazwa pakietu musi się kończyć przyrostkiem Bundle. Poprawnymi nazwami pakietów są: Gajdaw/HelloworldBundle GW/HelloBundle Gajda/HelloWorldBundle

Komenda app/console generate:bundle działa w sposób interaktywny i umożliwia dostosowanie kilku parametrów generowanego pakietu. Możemy:  zmienić folder, w którym zostanie umieszczony pakiet;  ustalić sposób konfiguracji pakietu (YML, XML, PHP lub adnotacje2);  włączyć tworzenie kompletnej struktury pakietu (po włączeniu tej opcji

wygenerowane zostaną m.in. puste foldery public/css/ oraz public/images/ przeznaczone na style CSS oraz obrazy);  dołączyć nowy pakiet do konfiguracji aplikacji;  zmodyfikować reguły routingu adresów URL tak, by uwzględniony został

nowo tworzony pakiet. Interaktywny generator podpowiada domyślne wartości opcji: Bundle name [MyHelloworldBundle]: Target directory [...\hello-world/src]: Configuration format (yml, xml, php, or annotation) [annotation]: Do you want to generate the whole directory structure [no]? Do you confirm generation [yes]? Confirm automatic update of your Kernel [yes]? Confirm automatic update of the Routing [yes]?

2

Adnotacje są specjalnymi komentarzami umieszczanymi w kodzie PHP.

Rozdział 2. ♦ Hello, world!

31

Na wszystkie powyższe pytania możemy odpowiedzieć, naciskając przycisk Enter. Opcje przyjmą wówczas wartości podane w nawiasach kwadratowych. W celu wygenerowania nowego pakietu w sposób wsadowy (tj. bez trybu interaktywnego) wydaj komendę app/console generate:bundle w sposób następujący: php app/console generate:bundle --namespace=My/HelloworldBundle --dir=src ´--no-interaction

Wymaganymi parametrami polecenia generate:bundle są:  --namespace — parametr ustalający nazwę pakietu;  --dir — parametr ustalający położenie generowanego pakietu.

Ostatnia z opcji, --no-interaction, wyłącza tryb interaktywny.

Krok 3.2. Sprawdź strukturę wygenerowanego pakietu Po wydaniu komendy app/console generate:bundle w folderze hello-world/src/ utworzony zostanie pakiet, którego foldery i pliki przedstawiono na rysunku 2.2. Rysunek 2.2. Foldery i pliki utworzone po wydaniu komendy app/console generate:bundle

Bezpośrednio w folderze hello-world/src/ znajduje się plik .htaccess zawierający instrukcję: deny from all

Blokuje on dostęp do zawartości folderu hello-world/src/ za pomocą protokołu HTTP3. Pakiet hello-world/src/My/HelloworldBundle/ zawiera następujące foldery: 3

Innymi słowy do folderu hello-world/src/ nie da się zajrzeć za pomocą przeglądarki.

Część I ♦ Tworzenie prostych stron WWW

32

 Controller/ — folder przeznaczony na kontrolery;  DependencyInjection/ — folder konfigurujący zależności pomiędzy

obiektami;  Resources/ — folder zawierający zasoby pakietu (m.in. szablony, style CSS

oraz obrazy);  Tests/ — folder przeznaczony na testy jednostkowe.

Jeśli na pytanie: Do you want to generate the whole directory structure [no]?

odpowiesz: yes

wówczas w folderze Resources/ wygenerowane zostaną dodatkowo foldery i pliki widoczne na rysunku 2.3. Folder doc/ jest przeznaczony na dokumentację, public/ — na style CSS, skrypty JavaScript oraz obrazy, a translations/ — na pliki tłumaczeń. Rysunek 2.3. Kompletna struktura zasobów z folderu Resources/

Krok 3.3. Sprawdź zawartość pliku app/AppKernel.php Opcja konfiguracyjna: Confirm automatic update of your Kernel [yes]?

powoduje włączenie nowo tworzonego pakietu w konfiguracji aplikacji. W pliku hello-world/app/AppKernel.php w tablicy $bundles utworzony zostanie obiekt klasy MyHelloworldBundle: $bundles = array( ... new My\HelloworldBundle\MyHelloworldBundle(), );

Plik AppKernel.php jest przedstawiony na listingu 2.2.

Rozdział 2. ♦ Hello, world!

33

Listing 2.2. Plik AppKernel.php

MyHelloworldBundle:Default:index

5

Adnotacje @Template() omówimy szczegółowo w części poświęconej szablonom Twig.

Część I ♦ Tworzenie prostych stron WWW

46

Format PHP Po wybraniu formatu konfiguracji PHP konfiguracja routingu zostanie zapisana w pliku: src\My\HelloworldBundle\Resources\config\routing.php

w formacie: $entities); } }

Krok 8. Dostosuj widok akcji index Utwórz plik src/My/FrontendBundle/Resources/views/Kontynent/index.html.twig o zawartości takiej jak na listingu 32.16. Listing 32.16. Widok akcji index {% extends "::layout.html.twig" %} {% block content %} Lista wszystkich kontynentów

Część VII ♦ Relacje

356

    {% for kontynent in entities %}
  • {{ kontynent }}
      {% for panstwo in kontynent.panstwa %}
    • {{ panstwo }}
    • {% endfor %}
  • {% endfor %}
{% endblock %}

Krok 9. Przygotuj kontroler Panstwo W analogiczny sposób przygotuj kontroler Panstwo zawierający akcję index.

Krok 10. Dostosuj skórkę aplikacji Utwórz plik layout.html.twig i umieść w nim kod widoczny na listingu 32.17. Listing 32.17. Zawartość pliku layout.html.twig {% extends "::base.html.twig" %} {% block body %}
  • Strona główna
  • Lista kontynentów
  • Lista państw
{% block content %} {% endblock %} {% endblock %}

Krok 11. Wykonaj stronę główną Zmodyfikuj kod akcji index w kontrolerze Default. Zarys kontrolera DefaultController jest przedstawiony na listingu 32.18. Listing 32.18. Akcja index kontrolera default class DefaultController extends Controller { /** * @Route("/", name="homepage") * @Template() */

Rozdział 32. ♦ Relacje 1:n (jeden do wielu)

357

public function indexAction() { return array(); } }

W widoku akcji index wprowadź jeden akapit tekstu Lorem ipsum.

Krok 12. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Powinieneś ujrzeć witrynę przedstawioną na rysunku 32.1. Rysunek 32.1. Witryna z przykładu 32.1

Porządkowanie rekordów Kolekcja rekordów zależnych relacji 1:n, która jest zwracana przez metodę getPanstwa(), może być automatycznie sortowana dowolnymi kolumnami. W tym celu należy w klasie Kontynent dodać adnotację @ORM\OrderBy przedstawioną na listingu 32.19.

Część VII ♦ Relacje

358

Listing 32.19. Automatyczne sortowanie rekordów zwracanych przez metodę getPanstwa() class Kontynent { ... /** * @ORM\OneToMany(targetEntity="Panstwo", mappedBy="kontynent") * @ORM\OrderBy({"nazwa" = "ASC"}) */ protected $panstwa; ... }

Parametrem adnotacji @ORM\OrderBy jest tablica asocjacyjna, w której indeksami są nazwy właściwości w klasie Panstwo, a wartościami — ciągi ASC lub DESC.

Rozdział 33.

Relacje n:m (wiele do wielu) Relacja n:m wiąże rekordy zawarte w dwóch tabelach w następujący sposób:  Każdemu rekordowi z pierwszej tabeli może być przyporządkowana dowolna

liczba rekordów z drugiej tabeli.  Każdemu rekordowi z drugiej tabeli może być przyporządkowana dowolna

liczba rekordów z pierwszej tabeli. Powiązanie takie jest realizowane przez utworzenie dodatkowej tabeli, nazywanej tabelą łączącą relacji, w której zawarte są informacje o powiązaniach. Jako przykład ilustrujący relację wiele do wielu przeanalizujmy bazę danych zawierającą zestawienie aktorów i filmów. W bazie danych występują dwie tabele: aktor oraz film. Tabela aktor zawiera kolumny:  id — klucz główny;  imie — string o długości 255 znaków;  nazwisko — string o długości 255 znaków.

Tabela film zawiera kolumny:  id — klucz główny;  tytul — string o długości 255 znaków.

Tabele aktor i film są przedstawione na listingu 33.1. Listing 33.1. Struktura tabel aktor oraz film Tabela aktor id imie nazwisko

- klucz główny typu integer - string o długości 255 - string o długości 255

Część VII ♦ Relacje

360 Tabela film id tytul

- klucz główny typu integer - string o długości 255

Powiązanie aktorów i filmów relacją wiele do wielu polega na tym, że:  Każdemu filmowi może być przyporządkowana dowolna liczba aktorów.  Każdemu aktorowi może być przyporządkowana dowolna liczba filmów.

Informacje o powiązaniach relacyjnych rekordów będziemy zapisywali w tabeli łączącej o nazwie film_aktor. Wystąpią w niej dwie kolumny:  film_id — klucz obcy z tabeli film_id;  aktor_id — klucz obcy z tabeli aktor_id.

Wprowadźmy przykładowe rekordy w tabeli film: id 9 17

tytul Miś Kanał

oraz w tabeli aktor: id 3 11 25

imie Stanisław Stanisław Krzysztof

nazwisko Mikulski Tym Kowalewski

W celu powiązania rekordu Miś z rekordem Stanisław Tym należy w tabeli film_aktor umieścić rekord: film_id 9

aktor_id 11

W analogiczny sposób powiązanie rekordów Kanał oraz Stanisław Mikulski realizuje rekord: film_id 17

aktor_id 3

Użycie relacji n:m w Symfony 2 W Symfony 2 relacje n:m definiujemy adnotacjami: @ORM\ManyToMany(...)

Powiązanie tabel film oraz aktor jest zilustrowane na listingach 33.2 oraz 33.3.

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

361

Listing 33.2. Właściwość i adnotacja definiujące relację n:m w klasie Film class Film { ... /** * @ORM\ManyToMany(targetEntity="Aktor", inversedBy="filmy") */ private $aktorzy; ... }

Listing 33.3. Właściwość i adnotacja definiujące relację n:m w klasie Aktor class Aktor { ... /** * @ORM\ManyToMany(targetEntity="Film", mappedBy="aktorzy") */ protected $filmy; ... }

Po dodaniu w klasach Film oraz Kontynent właściwości z listingów 33.2 oraz 33.3 wydajemy polecenie: php app/console generate:doctrine:entities My

Spowoduje ono dodanie:  w klasie Film metod __construct(), addAktor() i getAktorzy();  w klasie Aktor metod __construct(), addFilm() i getFilmy().

Właściciel relacji n:m Relacja występująca na listingach 33.2 oraz 33.3 jest relacją dwukierunkową, w której klasa Film jest właścicielem relacji (ang. owing side), a klasa Aktor — klasą odwrotną relacji (ang. inverse side). Klasę, która jest właścicielem relacji, rozpoznajemy po wystąpieniu parametru inversedBy w adnotacji definiującej powiązanie: @ORM\ManyToMany(targetEntity="Aktor", inversedBy="filmy")

Konsekwencją jest to, że aktualizacja powiązań rekordów powinna być wykonywana w obiektach klasy Film.

Część VII ♦ Relacje

362

Tabela łącząca relacji n:m Domyślnie tabela łącząca relacji n:m otrzyma nazwę, która powstaje przez połączenie nazw tabel połączonych relacją. W omawianym przykładzie tabela łącząca otrzyma nazwę: film_aktor

Pierwszym członem jest nazwa klasy, która jest właścicielem relacji. Po wydaniu polecenia: php app/console doctrine:schema:create

w bazie danych pojawią się trzy tabele:  film,  aktor,  film_aktor.

W celu ustalenia innej nazwy dla tabeli łączącej należy w klasie, która jest właścicielem relacji, dodać przedstawioną na listingu 33.4 adnotację @ORM\JoinTable. Listing 33.4. Adnotacja ustalająca nazwę tabeli łączącej class Film { ... /** * @ORM\ManyToMany(targetEntity="Aktor", inversedBy="filmy") * @ORM\JoinTable(name="film_has_aktor") */ private $aktorzy; }

...

Operowanie rekordami powiązanymi relacją Tworzenie rekordów Listing 33.5 ilustruje procedurę wstawiania do bazy danych rekordów Miś oraz Stanisław Tym. Zwróć uwagę, że powiązanie obiektów wykonujemy, wywołując metodę addAktor() klasy Film. Użycie metody addFilm() klasy Aktor będzie błędem: zmiany dotyczące relacji i wykonane w obiektach klasy Aktor zostaną utracone.

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

363

Listing 33.5. Wstawianie powiązanych rekordów Miś oraz Stanisław Tym $Film = new Film(); $manager->persist($Film); $Film->setTytul('Miś'); $Aktor = new Aktor(); $manager->persist($Aktor); $Aktor->setImie('Stanisław'); $Aktor->setNazwisko('Tym'); $Film->addAktor($Aktor); $manager->flush();

Nazwy metod addAktor() oraz addFilm() powstają na podstawie nazw klas Aktor oraz Film.

Rekordy zależne Listę rekordów klasy Aktor powiązanych z danym rekordem klasy Film zwraca metoda getAktorzy(). Kod z listingu 33.6 drukuje imiona i nazwiska wszystkich aktorów występujących w filmie pt. „Miś”. Listing 33.6. Wydruk aktorów występujących w zadanym filmie $Film = $manager ->getRepository('MyFrontendBundle:Film') ->findOneByTytul('Miś'); foreach ($Film->getAktorzy() as $Aktor) { echo $Aktor->getImie(); echo $Aktor->getNazwisko(); }

Listę rekordów klasy Film powiązanych z danym rekordem klasy Aktor zwraca metoda getFilmy(). Kod z listingu 33.7 drukuje tytuły wszystkich filmów, w których wystąpił Robert Redford. Listing 33.7. Wydruk aktorów występujących w zadanym filmie $Aktor = $manager ->getRepository('MyFrontendBundle:Aktor') ->findOneBy( array('imie' => 'Robert', 'nazwisko' => 'Redford') ); foreach ($Aktor->getFilmy() as $Film) { echo $Film->getTytul(); }

Część VII ♦ Relacje

364

Nazwy metod getFilmy() oraz getAktorzy() powstają na podstawie nazw właściwości: private $aktorzy; private $filmy;

Synchronizacja relacji W celu zapewnienia synchronizacji powiązań relacyjnych bez względu na to, czy modyfikacje wykonujemy w obiektach klasy Film, czy klasy Aktor, należy w metodzie addFilm() klasy Aktor dodać widoczną na listingu 33.8 instrukcję addAktor(). Listing 33.8. Wywołanie metody addAktor() zapewni synchronizację powiązań relacyjnych definiowanych wywołaniem metody addFilm() class Aktor { ... /** * Add filmy * * @param My\FrontendBundle\Entity\Film $filmy */ public function addFilm(\My\FrontendBundle\Entity\Film $filmy) { $this->filmy[] = $filmy; $filmy->addAktor($this); } ... }

Usuwanie powiązania relacyjnego W celu usunięcia relacji łączącej dwa obiekty należy wywołać metodę removeElement() kolekcji po stronie klasy będącej właścicielem relacji. Przykładowy kod ilustrujący usuwanie powiązania rekordów Wielki Gatsby oraz Robert Redford jest przedstawiony na listingu 33.9. Listing 33.9. Usuwanie powiązania relacyjnego pomiędzy rekordami Wielki Gatsby oraz Robert Redford $Film = $manager ->getRepository('MyFrontendBundle:Film') ->findOneByTytul('Wielki Gatsby'); $Aktor = $manager ->getRepository('MyFrontendBundle:Aktor') ->findOneBy(array('imie' => 'Robert', 'nazwisko' => 'Redford'));

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

365

$Film->getAktorzy()->removeElement($Aktor); $manager->flush();

Należy pamiętać, że usunięcie powiązania wykonujemy po stronie klasy będącej właścicielem relacji. Dodanie instrukcji z listingu 33.8 powoduje synchronizację wyłącznie podczas tworzenia powiązania relacyjnego.

Akcje referencyjne SQL Akcje SQL-owe Relacje n:m mają domyślnie włączone akcje referencyjne SQL, które powodują, że usunięcie rekordu z jednej z tabel powiązanych (tj. film lub aktor) spowoduje automatyczne usunięcie odpowiednich rekordów z tabeli łączącej film_aktor.

Przykład 33.1. Filmy i aktorzy Dany jest plik filmy.xml, który zawiera listę filmów oraz aktorów. Przygotuj bazę danych filmy, która będzie pozwalała na zapisywanie danych o filmach i aktorach z zachowaniem informacji o tym, w których filmach wystąpił każdy z aktorów. Napisz skrypt, który wypełni bazę danymi odczytanymi z pliku XML. Następnie wykonaj witrynę WWW, która będzie zawierała dwie strony: listę wszystkich filmów oraz listę wszystkich aktorów. Listę wszystkich filmów wykonaj jako stronę akcji index w kontrolerze Film, a listę wszystkich aktorów — jako stronę akcji index w kontrolerze Aktor. Zadanie wykonaj w taki sposób, by na liście wszystkich filmów obok tytułu filmu widoczna była lista wszystkich aktorów, którzy wystąpili w danym filmie. Na stronie z listą wszystkich aktorów obok imienia i nazwiska każdego aktora umieść natomiast tytuły filmów, w których wystąpił.

ROZWIĄZANIE Krok 1. Utwórz nowy projekt i pakiet W folderze przeznaczonym na aplikacje WWW utwórz folder zad-33/ i wypakuj do niego zawartość archiwum symfony2-customized-v3.zip. Następnie utwórz pakiet My/FrontendBundle oraz w folderze data/ umieść plik XML.

Część VII ♦ Relacje

366

Krok 2. Utwórz bazę danych filmy Utwórz bazę danych o nazwie filmy oraz konto dostępu editor, po czym w pliku parameters.ini zmodyfikuj opcje ustalające parametry połączenia z bazą.

Krok 3. Wygeneruj klasy dostępu do bazy danych Wygeneruj modele: MyFrontendBundle:Film MyFrontendBundle:Aktor

Klasa Film powinna zawierać właściwość:  tytul typu string o długości 255 znaków.

Klasa Aktor powinna zawierać dwie właściwości:  imie typu string o długości 255 znaków,  nazwisko typu string o długości 255 znaków.

W wygenerowanych klasach dodaj metody __toString(). Metoda klasy Film ma zwracać tytuł, a metoda klasy Aktor — imię i nazwisko połączone spacją.

Krok 4. Zdefiniuj dwukierunkową relację n:m łączącą klasy Film oraz Aktor W klasie Film dodaj przedstawioną na listingu 33.2 właściwość $aktorzy. W klasie Aktor dodaj przedstawioną na listingu 33.3 właściwość $filmy. Poleceniem: php app/console generate:doctrine:entities My

wygeneruj następujące metody:  w klasie Film: __construct(), addAktor() i getAktorzy();  w klasie Aktor: __construct(), addFilm() i getFilmy().

Krok 5. Utwórz tabele film, aktor i film_aktor Wydaj polecenie: php app/console doctrine:schema:update --force

W bazie danych pojawią się trzy tabele: film, aktor oraz film_aktor.

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

367

Krok 6. Wypełnij bazę danych zawartością odczytaną z pliku XML Przygotuj przedstawiony na listingu 33.10 plik LoadData.php, a następnie wydaj polecenie: php app/console

doctrine:fixtures:load

Listing 33.10. Plik LoadData.php z przykładu 33.1 function load(ObjectManager $manager) { $xml = simplexml_load_file('data/filmy.xml'); foreach ($xml->film as $f) { $Film = new Film(); $Film->setTytul($f->tytul); $manager->persist($Film); foreach ($f->aktorzy->aktor as $a) { $Aktor = $manager ->getRepository('MyFrontendBundle:Aktor') ->findOneBy(array('imie' => $a->imie, 'nazwisko' => $a->nazwisko)); if (!$Aktor) { $Aktor = new Aktor(); $Aktor->setImie($a->imie); $Aktor->setNazwisko($a->nazwisko); $manager->persist($Aktor); }; $Film->addAktor($Aktor); $manager->flush(); } } $manager->flush(); }

W pliku XML każdy film występuje dokładnie jeden raz. Z aktorami sytuacja wygląda jednak inaczej. Aktor, który występuje w dwóch filmach, pojawi się w pliku XML dwukrotnie, co ilustruje listing 33.11. Listing 33.11. Aktor, który występuje w dwóch filmach, pojawia się w pliku XML dwukrotnie

Żądło

Robert Redford

...

O jeden most za daleko

Robert Redford

...

Część VII ♦ Relacje

368

...

Wstawiając rekordy do tabeli aktor, musimy sprawdzić, czy podana osoba nie została wcześniej wstawiona. Wyszukiwanie aktora realizuje instrukcja: $Aktor = $manager ->getRepository('MyFrontendBundle:Aktor') ->findOneBy(array('imie' => $a->imie, 'nazwisko' => $a->nazwisko));

Jeśli podany rekord nie został odnaleziony, tworzymy nowy obiekt klasy Aktor: if (!$Aktor) { $Aktor = new Aktor(); $Aktor->setImie($a->imie); $Aktor->setNazwisko($a->nazwisko); $manager->persist($Aktor); };

Bardzo ważne jest, by po utworzeniu nowego rekordu w tabeli aktor i powiązaniu go z rekordem z tabeli film wywołać metodę flush(): $manager->flush();

Jeśli metoda flush() nie zostanie wywołana, to nowo utworzony obiekt $Aktor nie zostanie zapisany w bazie danych. W takiej sytuacji wszystkie instrukcje wyszukiwania: ...->findOneBy(...);

wykonywane w pętli zwrócą wynik false!

Krok 7. Dostosuj akcję index W folderze src/My/FrontendBundle/Controller/ utwórz plik FilmController.php zawierający jedną akcję indexAction(). Zarys kontrolera jest przedstawiony na listingu 33.12. Listing 33.12. Kontroler Film class FilmController extends Controller { /** * Lista wszystkich filmow * * @Route("/filmy.html", name="film_index") * @Template() */ public function indexAction() { $em = $this->getDoctrine()->getEntityManager(); $entities = $em->getRepository('MyFrontendBundle:Film')->findAll(); return array('entities' => $entities); } }

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

369

Krok 8. Dostosuj widok akcji index Utwórz plik src/My/FrontendBundle/Resources/views/Film/index.html.twig o zawartości takiej jak na listingu 33.13. Listing 33.13. Widok akcji index {% extends "::layout.html.twig" %} {% block content %} Lista wszystkich filmów
    {% for film in entities %}
  • {{ film }}
      {% for aktor in film.aktorzy %}
    • {{ aktor }}
    • {% endfor %}
  • {% endfor %}
{% endblock %}

Krok 9. Przygotuj kontroler Aktor W analogiczny sposób przygotuj kontroler Aktor zawierający akcję index.

Krok 10. Dostosuj skórkę aplikacji Utwórz plik layout.html.twig i umieść w nim kod widoczny na listingu 33.14. Listing 33.14. Zawartość pliku layout.html.twig {% extends "::base.html.twig" %} {% block body %}
  • Strona główna
  • Lista aktorów
  • Lista filmów
{% block content %} {% endblock %} {% endblock %}

Część VII ♦ Relacje

370

Krok 11. Utwórz stronę główną Zmodyfikuj kod akcji index w kontrolerze Default tak, generowana strona zawierała akapit Lorem ipsum i miała adres /.

Krok 12. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Powinieneś ujrzeć witrynę przedstawioną na rysunku 33.1. Rysunek 33.1. Witryna z przykładu 33.1

Porządkowanie rekordów Kolekcję rekordów zależnych relacji n:m możemy porządkować przedstawioną na listingu 33.15 adnotacją @ORM\OrderBy. Listing 33.15. Automatyczne sortowanie rekordów zwracanych przez metodę getAktorzy() klasy Film class Film { ...

Rozdział 33. ♦ Relacje n:m (wiele do wielu)

/** * @ORM\ManyToMany(targetEntity="Aktor", inversedBy="filmy") * @ORM\OrderBy({"nazwisko"="ASC", "imie"="ASC"}) */ private $aktorzy; ... }

371

372

Część VII ♦ Relacje

Rozdział 34.

Relacje, akcje index i show oraz widoki częściowe Wykonując akcje show dla rekordów powiązanych relacjami, natrafimy wielokrotnie na problem redundancji kodu w widokach. Takie same pętle przetwarzające kolekcje obiektów pojawią się w akcjach index rekordów zależnych i w akcjach show rekordów nadrzędnych. Przeanalizujmy przykład z rozdziału 32. Jeśli w aplikacji dla obu klas Kontynent i Panstwo wykonamy akcje index oraz show prezentujące listę wszystkich rekordów oraz szczegółowe dane rekordu, wówczas:  Na stronie akcji kontynent/show pojawi się lista państw z danego kontynentu.  Na stronie akcji panstwo/index pojawi się lista wszystkich państw. Terminem: kontynent/show

określam skrótowo akcję showAction() z kontrolera KontynentController.

W celu uniknięcia powielania kodu widoku odpowiedzialnego za przetwarzanie listy wszystkich państw należy w folderze /views/Panstwo/ przygotować widok przedstawiony na listingu 34.1. Listing 34.1. Widok prezentujący kolekcję obiektów klasy Panstwo w postaci listy hiperłączy
    {% for panstwo in dane %}


  • {{ panstwo }}

    Część VII ♦ Relacje

    374

  • {% endfor %}


Widok ten możemy umieścić w dowolnym widoku, przekazując do niego kolekcję obiektów klasy Panstwo. Aby użyć widoku z listingu 34.1 w widoku akcji kontynent/show, należy użyć instrukcji z listingu 34.2. Listing 34.2. Użycie widoku z listingu 34.1 wewnątrz widoku akcji kontynent/show {% block content %} Szczegółowe dane kontynentu Nazwa: {{ entity }} Państwa z podanego kontynentu: {% include 'MyFrontendBundle:Panstwo:_list.html.twig' with {'dane': entity.panstwa } %} {% endblock %}

Jeszcze więcej redundancji wystąpi w przypadku akcji show wykonanych dla relacji n:m. Jeśli w przykładzie z rozdziału 33. dodamy akcje film/show oraz aktor/show, wówczas:  Lista tytułów filmów pojawi się na stronach akcji:  film/index (lista wszystkich filmów);  aktor/show (lista filmów, w których zagrał wybrany aktor).  Lista nazwisk aktorów pojawi się na stronach akcji:  aktor/index (lista wszystkich aktorów);  film/show (lista aktorów, którzy zagrali w danym filmie).

Powielanie kodu prezentującego listę rekordów klasy Aktor wyeliminujemy, przygotowując dwa widoki częściowe:  w folderze /views/Aktor/ widok _list.html.twig,  w folderze /views/Film/ widok _list.html.twig.

Kod widoku /views/Aktor/_list.html.twig jest przedstawiony na listingu 34.3. Listing 34.3. Widok częściowy /views/Aktor/_list.html.twig prezentujący listę aktorów
    {% for aktor in dane %}


  • {{ aktor }}

  • {% endfor %}


Rozdział 34. ♦ Relacje, akcje index i show oraz widoki częściowe

375

Widok z listingu 34.3 wywołujemy w widoku akcji aktor/index w sposób przedstawiony na listingu 34.4. Listing 34.4. Wywołanie widoku /views/Aktor/_list.html.twig w widoku akcji aktor/index {% block content %} Lista wszystkich aktorów {% include 'MyFrontendBundle:Aktor:_list.html.twig' with {'dane': entities } %} {% endblock %}

Ten sam widok będzie wywołany na stronie akcji film/show w sposób przedstawiony na listingu 34.5. Listing 34.5. Wywołanie widoku /views/Aktor/_list.html.twig w widoku akcji film/show {% block content %} Szczegółowe dane filmu Tytuł: {{ entity }} Aktorzy, którzy zagrali w filmie: {% include 'MyFrontendBundle:Aktor:_list.html.twig' with {'dane': entity.aktorzy } %} {% endblock %}

W analogiczny sposób przygotujemy widok prezentujący listę tytułów filmów i użyjemy go w widokach akcji film/index oraz aktor/show.

Przykład 34.1. Kontynenty/Państwa — akcje show i widoki częściowe Zmodyfikuj przykład z rozdziału 32. W projekcie dodaj akcje kontynent/show oraz panstwo/show. Wykonaj dwa widoki częściowe:  /views/Kontynent/_list.html.twig — widok generujący listę nazw

kontynentów;  /views/Panstwo/_list.html.twig — widok generujący listę nazw państw.

Widok /views/Kontynent/_list.html.twig wywołaj w kodzie akcji kontynent/index. Widok /views/Panstwo/_list.html.twig wywołaj w kodzie akcji kontynent/show oraz panstwo/index.

Część VII ♦ Relacje

376

Przykład 34.2. Filmy/Aktorzy — akcje show i widoki częściowe Zmodyfikuj przykład z rozdziału 33. W projekcie dodaj akcje aktor/show oraz film/show. Wykonaj dwa widoki częściowe:  /views/Aktor/_list.html.twig — widok generujący listę aktorów;  /views/Film/_list.html.twig — widok generujący listę tytułów filmów.

Widok /views/Aktor/_list.html.twig wywołaj w kodzie akcji aktor/index oraz film/show. Widok /views/Film/_list.html.twig wywołaj w kodzie akcji film/index oraz aktor/show.

Przykład 34.3. Powieści Agaty Christie Plik novels.xml zawiera informacje o powieściach Agaty Christie. Przygotuj aplikację, która dane dotyczące twórczości Agaty Christie udostępni w postaci witryny WWW. W serwisie wykonaj:  trzy modele: Novel, Detective i Method;  trzy kontrolery: NovelController, DetectiveController i MethodController;  oraz akcje:  Novel/index — strona prezentująca listę wszystkich powieści;  Novel/show — strona prezentująca szczegółowe dane pojedynczej

powieści;  Detective/index — strona prezentująca listę wszystkich detektywów;  Detective/show — strona prezentująca szczegółowe dane pojedynczego

detektywa;  method/index — strona prezentująca listę wszystkich metod zbrodni;  method/show — strona prezentująca szczegółowe dane pojedynczej metody

zbrodni. Modele Novel oraz Method połącz relacją wiele do wielu. Modele Detective oraz Novel połącz relacją jeden do wielu. Na stronie akcji Novel/show umieść:  tytuł powieści,  nazwisko detektywa,

Rozdział 34. ♦ Relacje, akcje index i show oraz widoki częściowe

377

 listę wszystkich metod zbrodni z danej powieści.

Na stronie akcji Detective/show umieść:  nazwisko detektywa,  listę wszystkich powieści z udziałem detektywa.

Na stronie akcji Method/show umieść:  nazwę metody,  listę wszystkich powieści, w których wystąpiła podana metoda zbrodni.

Do adresowania akcji show we wszystkich trzech kontrolerach użyj automatycznie generowanych ciągów slug. Dane zawarte w pliku XML nie są posortowane. Zwróć uwagę, by na wszystkich stronach aplikacji zestawienie powieści było posortowane alfabetycznie. Nie powielaj kodu odpowiedzialnego za prezentację listy tytułów powieści. Użyj do tego widoków częściowych. Wykorzystaj dane oraz szablon z pliku 34-03-start.zip.

ROZWIĄZANIE Krok 1. Utwórz nowy projekt i pakiet W folderze przeznaczonym na aplikacje WWW utwórz folder zad-34/ i wypakuj do niego zawartość archiwum symfony2-customized-v3.zip. Następnie utwórz pakiet My/FrontendBundle oraz w folderze data/ umieść plik XML.

Krok 2. Utwórz bazę danych achristie Utwórz bazę danych o nazwie achristie oraz konto dostępu editor, po czym w pliku parameters.ini zmodyfikuj opcje ustalające parametry połączenia z bazą.

Krok 3. Wygeneruj klasy dostępu do bazy danych Wygeneruj modele: MyFrontendBundle:Novel MyFrontendBundle:Detective MyFrontendBundle:Method

Klasa Novel powinna zawierać:  właściwość title typu string o długości 255 znaków.

Klasa Detective powinna zawierać:  dwie właściwości name typu string o długości 255 znaków.

Część VII ♦ Relacje

378

Klasa Method powinna zawierać:  dwie właściwości name typu string o długości 255 znaków.

Krok 4. Dostosuj modele W wygenerowanych modelach dodaj:  metody __toString() zwracające właściwość title lub name,  adnotację, która spowoduje wygenerowanie klas Repository.

Adnotacja, którą należy umieścić w klasie Novel, ma postać: @ORM\Entity(repositoryClass="My\FrontendBundle\Entity\NovelRepository")

Krok 5. Zdefiniuj relację 1:n łączącą klasy Detective i Novel W klasie Detective dodaj właściwość z listingu 34.6, a w klasie Novel — właściwość z listingu 34.7. Listing 34.6. Modyfikacja klasy Detective class Detective { ... /** * @ORM\OneToMany(targetEntity="Novel", mappedBy="detective") * @ORM\OrderBy({"title" = "ASC"}) */ protected $novels; }

Listing 34.7. Modyfikacja klasy Novel class Novel { ... /** * @ORM\ManyToOne(targetEntity="Detective", inversedBy="novels") */ protected $detective; ... }

Właściwości i adnotacje z listingów 34.3 oraz 34.4 definiują dwukierunkową relację 1:n, w której właścicielem relacji jest klasa Novel.

Rozdział 34. ♦ Relacje, akcje index i show oraz widoki częściowe

379

Krok 6. Zdefiniuj relację n:m łączącą klasy Novel i Method W klasie Novel dodaj właściwość z listingu 34.8, a w klasie Method — właściwość z listingu 34.9. Listing 34.8. Modyfikacja klasy Novel class Novel { ... /** * @ORM\ManyToMany(targetEntity="Method", inversedBy="novels") */ private $methods; ... }

Listing 34.9. Modyfikacja klasy Method class Method { ... /** * @ORM\ManyToMany(targetEntity="Novel", mappedBy="methods") * @ORM\OrderBy({"title" = "ASC"}) */ protected $novels; ... }

Właściwości i adnotacje z listingów 34.5 oraz 34.6 definiują dwukierunkową relację n:m, w której właścicielem relacji jest klasa Novel.

Krok 7. Włącz generowanie identyfikatorów slug W konfiguracji projektu włącz generowanie ciągów slug. Następnie w klasach Novel, Detective oraz Method dodaj właściwość slug opatrzoną odpowiednimi adnotacjami.

Krok 8. Wygeneruj i dostosuj klasy Repository Wydaj polecenie: php app/console generate:doctrine:entities My

a następnie w wygenerowanych klasach Repository nadpisz metodę findAll().

Część VII ♦ Relacje

380

Krok 9. Utwórz tabele bazy danych Wydaj polecenie: php app/console doctrine:schema:update --force

W bazie danych pojawią się cztery tabele: novel, detective, method oraz novel_method.

Krok 10. Wypełnij bazę danych zawartością odczytaną z pliku XML Przygotuj przedstawiony na listingu 34.10 skrypt LoadData.php, a następnie wydaj polecenie: php app/console

doctrine:fixtures:load

Listing 34.10. Plik LoadData.php z przykładu 34.1 function load(ObjectManager $manager) { $xml = simplexml_load_file('data/novels.xml'); foreach ($xml->novel as $n) { $Detective = $manager ->getRepository('MyFrontendBundle:Detective') ->findOneByName($n->detective); if (!$Detective) { $Detective = new Detective(); $Detective->setName($n->detective); $manager->persist($Detective); $manager->flush(); }; $Novel = new Novel(); $Novel->setTitle($n->title); $Novel->setDetective($Detective); $manager->persist($Novel); $manager->flush(); foreach ($n->methods->method as $m) { $Method = $manager ->getRepository('MyFrontendBundle:Method') ->findOneByName($m); if (!$Method) { $Method = new Method(); $Method->setName($m); $manager->persist($Method); $manager->flush(); };

}

}

}

$Novel->addMethod($Method); $manager->flush();

Rozdział 34. ♦ Relacje, akcje index i show oraz widoki częściowe

381

Po wykonaniu polecenia: php app/console

doctrine:fixtures:load

w bazie danych powinno pojawić się 236 rekordów.

Krok 11. Wykonaj trzy kontrolery Utwórz plik NovelController.php o zawartości przedstawionej na listingu 34.11. Listing 34.11. Kontroler Novel /** * Novel controller. * * @Route("/novel") */ class NovelController extends Controller { /** * Lists all Novel entities. * * @Route("/index.html", name="novel") * @Template() */ public function indexAction() { $em = $this->getDoctrine()->getEntityManager(); $entities = $em->getRepository('MyFrontendBundle:Novel')->findAll(); return array('entities' => $entities); } /** * Finds and displays a Novel entity. * * @Route("/{slug}.html", name="novel_show") * @Template() */ public function showAction($slug) { $em = $this->getDoctrine()->getEntityManager(); $entity = $em->getRepository('MyFrontendBundle:Novel')->findOneBySlug($slug); if (!$entity) { throw $this->createNotFoundException('Unable to find Novel entity.'); } return array('entity' => $entity); } }

Część VII ♦ Relacje

382

W analogiczny sposób przygotuj kontrolery DetectiveController.php oraz Method Controller.php.

Krok 12. Wykonaj widoki częściowe Utwórz plik src/My/FrontendBundle/Resources/views/Novel/_list.html.twig o zawartości takiej jak na listingu 34.12. Listing 34.12. Widok odpowiedzialny za prezentację kolekcji obiektów klasy Novel w postaci tabelki hiperłączy



{% for novel in entities %} {% endfor %}

Title


{{ novel }}



Utwórz plik src/My/FrontendBundle/Resources/views/Detective/_list.html.twig o zawartości takiej jak na listingu 34.13. Listing 34.13. Widok odpowiedzialny za prezentację kolekcji obiektów klasy Detective w postaci tabelki hiperłączy



{% for detective in entities %} {% endfor %}

Name


{{ detective }}



Rozdział 34. ♦ Relacje, akcje index i show oraz widoki częściowe

383

Utwórz plik src/My/FrontendBundle/Resources/views/Method/_list.html.twig o zawartości takiej jak na listingu 34.14. Listing 34.14. Widok odpowiedzialny za prezentację kolekcji obiektów klasy Method w postaci tabelki hiperłączy



{% for method in entities %} {% endfor %}

Name


{{ method }}



Krok 13. Wykonaj widoki akcji Novel/index W widoku akcji Novel/index umieść wywołanie widoku częściowego _list.html.twig z listingu 34.9. Kod widoku Novel/index jest przedstawiony na listingu 34.15. Listing 34.15. Widok akcji Novel/index {% extends '::layout.html.twig' %} {% block contents %} Novels {% include 'MyFrontendBundle:Novel:_list.html.twig' with {'entities': entities } %} {% endblock contents %}

Krok 14. Wykonaj widoki akcji Method/show W widoku akcji Novel/show umieść wywołanie widoku częściowego z listingu 34.9. Kod widoku Novel/index jest przedstawiony na listingu 34.16. Listing 34.16. Widok akcji Novel/index {% extends '::layout.html.twig' %} {% block contents %} Method



Część VII ♦ Relacje

384

Name {{ entity }}
Novels {% include 'MyFrontendBundle:Novel:_list.html.twig' with {'entities': entity.novels } %} {% endblock contents %}

W analogiczny sposób wykonaj pozostałe widoki akcji show oraz index.

Krok 15. Dostosuj skórkę aplikacji Pracę nad aplikacją zakończ, wykonując stronę powitalną z tekstem Lorem ipsum oraz modyfikując skórkę aplikacji. Wykorzystaj szablon zawarty w pliku 34-03-start.zip.

Krok 16. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Powinieneś ujrzeć witrynę przedstawioną na rysunku 34.1.

Rysunek 34.1. Witryna z przykładu 34.1

Rozdział 35.

Podsumowanie części VII W części tej omówiliśmy relacje łączące klasy modelu. Szczegółowo omówiliśmy relacje:  jeden do jednego,  jeden do wielu,  wiele do wielu.

W Doctrine 2 relacje definiujemy przy użyciu adnotacji zawartych w klasach Entity. Każda relacja łączy dwie klasy. Jedną z tych klas nazywamy właścicielem relacji (ang. owing side), a drugą — klasą odwrotną relacji (ang. inverse side). Właścicielem relacji jest zawsze klasa, w której występuje parametr adnotacji inversedBy. Pojęcia właściciela i klasy odwrotnej są istotne, gdyż aktualizację powiązań relacyjnych należy wykonywać w klasie, która jest właścicielem relacji. W przeciwnym przypadku modyfikacje zostaną utracone. Ostatni z rozdziałów przybliżył nam użycie poznanego w rozdziale 12. znacznika Twig: {% include %}. Wykorzystując znacznik include, możemy przygotowywać widoki częściowe, które wyeliminują redundancję kodu prezentującego rekordy zależne relacji.

386

Część VII ♦ Relacje

Część VIII

Panele CRUD i zabezpieczanie dostępu do aplikacji

388

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

389

Rozdział 36.

Generowanie paneli administracyjnych CRUD Panel administracyjny CRUD1 zapewnia możliwość wykonania czterech rodzajów operacji:  create — tworzenie nowych rekordów;  read/retrieve — odczytywanie rekordów;  update — uaktualnianie rekordów;  delete/destroy — usuwanie rekordów.

W Symfony 2 panele CRUD generujemy poleceniem: php app/console generate:doctrine:crud

Po wydaniu komendy generate:doctrine:crud należy ustalić nazwę modelu, dla którego generujemy panel administracyjny. Jeśli w odpowiedzi na monit: The Entity shortcut name:

podamy nazwę: MyFrontendBundle:Name

wówczas panel administracyjny CRUD zostanie wygenerowany w pakiecie: My/FrontendBundle

dla klasy zawartej w pliku: My/FrontendBundle/Entity/Name.php

1

Szczegółowy opis operacji CRUD znajdziesz na stronie: http://pl.wikipedia.org/wiki/CRUD.

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

390

Wygenerowanie panelu administracyjnego polega na:  utworzeniu kontrolera,  utworzeniu widoków,  utworzeniu klas formularzy.

Dla modelu: MyFrontendBundle:Name

wygenerowane zostaną:  kontroler: src/My/FrontendBundle/Controller/NameController.php

 widoki w folderze: src/My/FrontendBundle/Resources/views/Name/

 formularz: src/My/FrontendBundle/Form/NameType.php

W zależności od odpowiedzi, jakiej udzielimy na kolejne pytanie: Do you want to generate the "write" actions [no]? yes

wygenerowane zostaną różne akcje. Jeśli na powyższe pytanie udzielimy odpowiedzi domyślnej no, wówczas wygenerowane zostaną wyłącznie akcje:  index — akcja wyświetlająca listę wszystkich rekordów;  show — akcja wyświetlająca szczegółowe dane rekordu.

Tak wygenerowany kontroler nie będzie pozwalał na:  tworzenie nowych rekordów,  modyfikację rekordów,  usuwanie rekordów. Akcje index oraz show, które występowały w części VI, możemy generować poleceniem: php app/console generate:doctrine:crud

Jeśli na pytanie: Do you want to generate the "write" actions [no]? yes

odpowiemy yes, wówczas wygenerowane zostaną akcje zapewniające pełny zestaw operacji CRUD:  index — akcja wyświetlająca listę wszystkich rekordów;  show — akcja wyświetlająca szczegółowe dane rekordu;

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

391

 new — akcja wyświetlająca formularz do tworzenia nowego rekordu;  create — akcja przetwarzająca formularz z akcji new;  edit — akcja wyświetlająca formularz do edycji rekordu;  update — akcja przetwarzająca formularz z akcji edit;  delete — akcja usuwająca wybrany rekord.

Akcje index, show, new oraz edit będą miały własne widoki: index.html.twig show.html.twig new.html.twig edit.html.twig

Akcja create jest poprzedzona następującą adnotacją @Template(): @Template("MyFrontendBundle:Name:new.html.twig") public function createAction()

zatem wykorzystuje ona widok akcji new. W analogiczny sposób akcja update wykorzystuje widok akcji edit: @Template("MyFrontendBundle:Name:edit.html.twig") public function updateAction($id)

Akcja delete kończy się przekierowaniem, dlatego nie stosuje żadnego widoku.

Adresy URL akcji CRUD Klasa kontrolera jest oznaczona adnotacją @Route przedstawioną na listingu 36.1. W ten sposób wszystkie adresy URL wymienione w akcjach kontrolera będą dzieliły wspólny przedrostek: /name

Listing 36.1. Adnotacja @Route klasy NameController /** * Name controller. * * @Route("/name") */ class NameController extends Controller { ... }

Akcje panelu CRUD będą oznaczone adnotacjami @Route przedstawionymi na listingu 36.2.

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

392

Listing 36.2. Adnotacje @Route akcji panelu CRUD wygenerowanego dla klasy Name @Route("/", name="name") public function indexAction() @Route("/{id}/show", name="name_show") public function showAction($id) @Route("/new", name="name_new") public function newAction() @Route("/create", name="name_create") public function createAction() @Route("/{id}/edit", name="name_edit") public function editAction($id) @Route("/{id}/update", name="name_update") public function updateAction($id) @Route("/{id}/delete", name="name_delete") public function deleteAction($id)

Akcja index Adresem akcji index będzie: .../web/name

Adres ten wygenerujemy, umieszczając w widoku instrukcję:

Lista wszystkich rekordów

Akcja show Adresem akcji show dla rekordu o wartości klucza głównego 123 będzie: .../web/name/123/show

Adres ten wygenerujemy, umieszczając w widoku instrukcję:

pokaż szczegółowe dane rekordu

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

393

Oczywiście do wygenerowania adresu możemy użyć obiektu klasy Name:

pokaż szczegółowe dane rekordu

Akcja new Adresem akcji new będzie: .../web/name/new

Adres ten wygenerujemy, umieszczając w widoku instrukcję:

Utwórz nowy rekord

Akcja create Adresem akcji create będzie: .../web/name/create

Adres ten jest generowany w widoku akcji new.html.twig do wskazania akcji odpowiedzialnej za przetwarzanie formularza:

Akcja edit Adresem akcji edit dla rekordu o wartości klucza głównego 123 będzie: .../web/name/123/edit

Adres ten wygenerujemy, umieszczając w widoku instrukcję:

Edytuj rekord

Oczywiście do wygenerowania adresu możemy użyć obiektu klasy Name:

Edytuj rekord

Akcja update Adresem akcji update uaktualniającej dane rekordu o wartości klucza głównego 123 będzie: .../web/name/123/update

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

394

Adres ten jest generowany w widoku akcji edit.html.twig w celu wskazania akcji odpowiedzialnej za przetwarzanie formularza:

Do wygenerowania adresu możemy użyć obiektu klasy Name:

Akcja delete Adresem akcji delete usuwającej rekord, dla którego id = 123, będzie: .../web/name/123/delete

Adres ten jest generowany w widoku akcji edit.html.twig:

Ponowne generowanie paneli CRUD Polecenie: php app/console generate:doctrine:crud

zawiera zabezpieczenie chroniące przed utratą modyfikacji wprowadzonych w panelu CRUD. Jeśli w pakiecie występuje już kontroler o nazwie pokrywającej się z generowanym panelem CRUD, wówczas generowanie zakończy się błędem: [RuntimeException] Unable to generate the controller as it already exists.

W celu ponownego wygenerowania panelu CRUD należy najpierw ręcznie usunąć istniejący kontroler.

Panele CRUD a relacje Panele CRUD zawierają podstawowe kontrolki umożliwiające edycję zależności relacyjnych. Edycja zależności relacyjnych odbywa się po stronie klasy, która jest właścicielem relacji.

Przykład 36.1. Imiona — panel CRUD Zmodyfikuj przykład z rozdziału 17. Wygeneruj w nim panel administracyjny umożliwiający edycję rekordów z tabeli Name.

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

395

ROZWIĄZANIE Krok 1. Usuń kontroler i widoki W gotowym projekcie z rozdziału 17. usuń plik My/FrontendBundle/Controller/ DefaultController.php oraz folder My/FrontendBundle/Resources/views/Default/.

Krok 2. Wygeneruj panel CRUD Wydaj polecenie: php app/console generate:doctrine:crud

W odpowiedzi na monit: The Entity shortcut name:

podaj nazwę: MyFrontendBundle:Name

Krok 3. Dostosuj adresy URL W wygenerowanym kontrolerze My/FrontendBundle/Controller/NameController.php usuń adnotację: @Route("/name")

która poprzedza klasę: class NameController extends Controller

Krok 4. Dostosuj etykiety formularza W klasie formularza My/FrontendBundle/Form/NameType.php dodaj parametr metody add(), który ustali etykietę pola zawierającego imię. Kod zmodyfikowanej metody buildForm() z pliku NameType.php jest przedstawiony na listingu 36.3. Listing 36.3. Modyfikacja etykiety formularza public function buildForm(FormBuilder $builder, array $options) { $builder ->add('caption', 'text', array('label' => 'Etykieta')) ; }

Krok 5. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

396

Po przejściu do edycji rekordu Abel ujrzysz stronę przedstawioną na rysunku 36.1. Rysunek 36.1. Witryna z przykładu 36.1

Oczywiście wszystkie komunikaty oraz cały kod HTML witryny możesz dostosować, modyfikując wygenerowane widoki.

Przykład 36.2. Panel CRUD i relacja 1:1 Zmodyfikuj przykład z rozdziału 31. Wygeneruj w nim panel administracyjny umożliwiający edycję rekordów z tabeli User.

ROZWIĄZANIE Krok 1. Usuń kontroler i widoki W gotowym projekcie z rozdziału 31. usuń plik My/FrontendBundle/Controller/ DefaultController.php oraz folder My/FrontendBundle/Resources/views/Default/.

Krok 2. Wygeneruj panel CRUD Wydaj polecenie: php app/console generate:doctrine:crud

W odpowiedzi na monit: The Entity shortcut name:

podaj nazwę: MyFrontendBundle:User

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

397

Krok 3. Dostosuj adresy URL W wygenerowanym kontrolerze My/FrontendBundle/Controller/UserController.php usuń adnotację: @Route("/name")

która poprzedza klasę: class UserController extends Controller

Krok 4. Dostosuj formularze edycyjne Domyślnie wygenerowany panel edycyjny dla relacji 1:1 nie pozwala edytować rekordów z tabeli profil. Problem ten możemy rozwiązać na dwa sposoby:  generując osobny panel CRUD dla modelu MyFrontendBundle:Profil,  osadzając formularz do edycji modelu MyFrontendBundle:Profil w formularzu do edycji modelu MyFrontendBundle:User.

Wykonajmy drugie z rozwiązań. Poleceniem: php app/console generate:doctrine:form MyFrontendBundle:Profil

wygeneruj plik My/FrontendBundle/Form/ProfilType.php, który będzie zawierał klasę formularza do edycji rekordów z tabeli profil. W wygenerowanej klasie ustal etykietę pola o nazwie info oraz dodaj metodę getDefaultOptions(). Zarys kodu z pliku ProfilType.php jest przedstawiony na listingu 36.4. Listing 36.4. Klasa formularza do edycji rekordów z tabeli ProfilType class ProfilType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('info', 'text', array('label' => 'Informacja o użytkowniku')) ; } public function getName() { return 'my_frontendbundle_profiltype'; } public function getDefaultOptions(array $options) { return array( 'data_class' => 'My\FrontendBundle\Entity\Profil', ); } }

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

398

Następnie w klasie UserType, która jest zawarta w pliku My/FrontendBundle/Form/ UserType.php, wprowadź modyfikacje widoczne na listingu 36.5. Klasa UserType została wygenerowana poleceniem generate:doctrine:crud. Listing 36.5. Modyfikacje klasy UserType class UserType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('name', 'text', array('label' => 'Imię użytkownika')) ->add('profil', new ProfilType(), array('label' => 'Profil użytkownika')) ; } public function getName() { return 'my_frontendbundle_usertype'; } }

Instrukcja: ->add('profil', new ProfilType(), array('label' => 'Profil użytkownika'))

modyfikuje pole profil zawarte w formularzu. Domyślnie pole to było wyświetlane w postaci listy rozwijanej pozwalającej na wybór rekordu z tabeli profil. Powyższa zmiana powoduje, że pole profil będzie osadzonym formularzem, który umożliwi edycję rekordów z tabeli profil.

Krok 5. Włącz kaskadowość operacji zapisu dla obiektów User W klasie Entity/User.php dodaj parametr cascade adnotacji @ORM\OneToOne: /** * * @ORM\OneToOne(targetEntity="Profil", inversedBy="user", cascade={"all"}) * @ORM\JoinColumn(name="profil_id", referencedColumnName="id") */ private $profil;

Dzięki temu podczas zapisywania do bazy danych obiektu klasy User zapisany zostanie także powiązany z nim relacyjnie obiekt klasy Profil.

Krok 6. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Po przejściu do edycji rekordu Jan ujrzysz stronę przedstawioną na rysunku 36.2.

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

399

Rysunek 36.2. Witryna z przykładu 36.2

Przykład 36.3. Panel CRUD i relacja 1:n Zmodyfikuj przykład z rozdziału 32. Wygeneruj w nim panel administracyjny umożliwiający edycję rekordów z tabel kontynent oraz panstwo.

ROZWIĄZANIE Krok 1. Usuń kontrolery i widoki W gotowym projekcie z rozdziału 32. usuń pliki KontynentController.php i PanstwoKontroler.php oraz foldery My/FrontendBundle/Resources/views/Kontynent/ i My/FrontendBundle/Resources/views/Panstwo/.

Krok 2. Wygeneruj panele CRUD Poleceniem: php app/console generate:doctrine:crud

wygeneruj panele administracyjne dla modeli: MyFrontendBundle:Kontynent MyFrontendBundle:Panstwo

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

400

Krok 3. Dostosuj widoki akcji We wszystkich wygenerowanych widokach, tj. w plikach z folderów My/FrontendBundle/ Resources/views/Kontynent/ i My/FrontendBundle/Resources/views/Panstwo/, dodaj instrukcje włączające dekorację szablonem layout.html.twig. Na początku każdego pliku dodaj instrukcje: {% extends "::layout.html.twig" %} {% block content %}

a na końcu: {% endblock %}

Krok 4. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Po przejściu do edycji rekordu Kolumbia z tabeli panstwo ujrzysz stronę przedstawioną na rysunku 36.3. Rysunek 36.3. Witryna z przykładu 36.3

Rozdział 36. ♦ Generowanie paneli administracyjnych CRUD

401

Przykład 36.4. Panel CRUD i relacja n:m Zmodyfikuj przykład z rozdziału 33. Wygeneruj w nim panel administracyjny umożliwiający edycję rekordów z tabel aktor oraz film.

ROZWIĄZANIE Krok 1. Usuń kontrolery i widoki W gotowym projekcie z rozdziału 33. usuń kontrolery AktorController.php i FilmController.php oraz ich widoki.

Krok 2. Wygeneruj panele CRUD Poleceniem: php app/console generate:doctrine:crud

wygeneruj panele administracyjne dla modeli: MyFrontendBundle:Aktor MyFrontendBundle:Film

Krok 3. Dostosuj widoki akcji We wszystkich wygenerowanych widokach dodaj instrukcje włączające dekorację szablonem layout.html.twig. Na początku każdego pliku dodaj instrukcje: {% extends "::layout.html.twig" %} {% block content %}

a na końcu: {% endblock %}

Krok 4. Sprawdź działanie aplikacji Odwiedź w przeglądarce adres: .../web/ Po przejściu do edycji rekordu Szczęki z tabeli film ujrzysz stronę przedstawioną na rysunku 36.4. Domyślnie kontrolki edycyjne pozwalają wyłącznie na modyfikację rekordów w tabeli, której klasa jest właścicielem relacji, czyli w tabeli film.

402 Rysunek 36.4. Witryna z przykładu 36.4

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

Rozdział 37.

Instalacja pakietu FOSUserBundle Do zabezpieczania dostępu do aplikacji wykorzystamy pakiet FOSUserBundle. Pracę z pakietem FOSUserBundle należy oczywiście rozpocząć od instalacji. Szczegółowy opis instalacji pakietu FOSUserBundle jest dostępny pod adresem: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/ Resources/doc/index.md

Przykład 37.1. Przygotowanie dystrybucji symfony2-customized-v4 zawierającej pakiet FOSUserBundle Przygotuj dystrybucję symfony2-customized-v4.zip, która będzie zawierała pakiet FOSUserBundle.

ROZWIĄZANIE Krok 1. Wypakuj dystrybucję przygotowaną w rozdziale 21. Wypakuj przygotowane w rozdziale 21. archiwum symfony2-customized-v3.zip, po czym zmień nazwę otrzymanego folderu na symfony2-customized-v4/.

Część VIII ♦ Panele CRUD i zabezpieczanie dostępu do aplikacji

404

Krok 2. Zmodyfikuj plik deps Na końcu pliku [projekt]/deps dodaj pakiet wymieniony na listingu 37.1. Listing 37.1. Zawartość, którą należy dodać na końcu pliku deps [FOSUserBundle] git=git://github.com/FriendsOfSymfony/FOSUserBundle.git target=bundles/FOS/UserBundle version=1.2.0

Krok 3. Pobierz pakiety Wydaj komendę: php bin/vendors install --reinstall

Krok 4. Usuń foldery .git Uruchom konsolę bash i przejdź do folderu symfony2-customized-v4/. Wydaj w nim komendę: find vendor -name .git -type d -exec rm -fr {} \;

Krok 5. Utwórz pakiet UserBundle Komendą: php app/console generate:bundle --namespace=My/UserBundle --dir=src --no-interaction

utwórz pakiet My/UserBundle. Następnie utwórz folder My/UserBundle/Entity/ i umieść w nim plik User.php o zawartości przedstawionej na listingu 37.2. W ten sposób w aplikacji wystąpi model o logicznej nazwie: MyUserBundle:User

Listing 37.2. Zawartość pliku My/UserBundle/Entity/User.php



My\FrontendBundle\Entity\City SonataAdminBundle:CRUD

MyFrontendBundle

Część IX ♦ Panele administracyjne Sonata

464



Krok 5. Przygotuj plik zawierający tłumaczenia Utwórz plik My/FrontendBundle/Resources/translations/MyFrontendBundle.pl.xliff, o zawartości takiej jak na listingu 43.3. Listing 43.3. Plik My/FrontendBundle/Resources/translations/MyFrontendBundle.pl.xliff



Dashboard Pulpit

Name Nazwa

Population Populacja

City List Lista miast

City Edit Edytuj miasto

City Delete Usuń miasto



Krok 6. Sprawdź wygląd panelu administracyjnego do edycji miast Po odwiedzeniu adresu: .../web/admin/washboard

i zalogowaniu na konto administratora ujrzysz panel przedstawiony na rysunku 43.1.

Rozdział 43. ♦ Użycie paneli administracyjnych Sonata do własnych tabel

Rysunek 43.1. Panel administracyjny do zarządzania rekordami z tabeli city

Menu wskazane strzałką jest zdefiniowane wpisami:

z listingu 43.2. Tłumaczenia, m.in.: Lista miast Populacja

powstają natomiast na podstawie wpisów z listingu 43.3:

Population Populacja

City List Lista miast

465

466

Część IX ♦ Panele administracyjne Sonata

Rozdział 44.

Podsumowanie części IX Rozdziały 42. oraz 43. opisują konfigurację oraz użycie pakietów z projektu Sonata. Dzięki nim możemy w prosty sposób wzbogacić aplikację o panele administracyjne. W ten sposób zakończyliśmy omawianie podstawowych możliwości oprogramowania Symfony 2. Na zakończenie warto przygotować ostatnią dystrybucję, symfony2-customized-v6.zip, która będzie zawierała wszystkie omówione w książce pakiety, czyli:  fikstury z rozdziału 15.,  zachowania Doctrine z części V,  pakiet FOSUserBundle z rozdziału 37.  oraz omówione w rozdziale 42. pakiety projektu Sonata.

Dzięki temu będziemy mogli w prosty sposób rozpocząć pracę nad kolejnym projektem.

Przykład 44.1. Przygotowanie dystrybucji symfony2-customized-v6 zawierającej omówione pakiety Przygotuj dystrybucję symfony2-customized-v6.zip, która będzie zawierała wszystkie pakiety omówione w książce.

Część IX ♦ Panele administracyjne Sonata

468

Przykład 44.2. Rzeki: aplikacja z panelem Sonata Wykonaj aplikację omówioną w rozdziale 18., wzbogacając ją o panel administracyjny Sonata. Do wykonania zadania wykorzystaj dystrybucję symfony2-customized-v6.zip.

ROZWIĄZANIE Krok 1. Połącz przykład 18. z dystrybucją symfony2-customized-v6.zip W przykładzie 18.1 wykonaliśmy pakiet My/FrontendBundle. W celu użycia tego pakietu w nowym projekcie należy:  skopiować folder My/ zawarty w przykładzie 18.1 do folderu src/

wypakowanej dystrybucji symfony2-customized-v6.zip;  w pliku AppKernel.php dodać instrukcję włączającą pakiet FrontendBundle: new My\FrontendBundle\MyFrontendBundle(),

 skopiować szablony zawarte w folderze app/Resources/views/ przykładu 18.1

do wypakowanej dystrybucji;  skopiować style CSS oraz pliki graficzne z folderu web/ przykładu 18.1

do wypakowanej dystrybucji;  w pliku app/config/routing.yml dodać na samej górze instrukcje włączające routing z pakietu FrontendBundle: MyFrontendBundle: resource: "@MyFrontendBundle/Controller/" type: annotation prefix: /

 w pliku app/config/parameters.ini skonfigurować dostęp do bazy danych;  na zakończenie skopiować folder z danymi data/.

Po tych operacjach wydajemy polecenia tworzące bazę danych: php app/console doctrine:schema:update --force php app/console doctrine:fixtures:load

i odwiedzamy adres: .../web/

Powinniśmy ujrzeć stronę z rysunku 18.1.

Rozdział 44. ♦ Podsumowanie części IX

469

Krok 2. Wykonaj panel Sonata Najpierw przygotowujemy klasę My/FrontendBundle/Admin/RiverAdmin.php o treści analogicznej do listingu 43.1. Następnie tworzymy plik My/FrontendBundle/Resources/ config/services.xml o zawartości analogicznej do listingu 43.2. Jako trzeci wykonujemy plik tłumaczeń My/FrontendBundle/Resources/translations/MyFrontendBundle.pl.xliff. Jego zawartość będzie analogiczna do listingu 43.3. W dalszej kolejności tworzymy konto administratora: php app/console fos:user:create admin [email protected] password --super-admin php app/console fos:user:promote admin --super

i w pliku layout.html.twig dodajemy odsyłacz do panelu administracyjnego: Panel administracyjny

Na zakończenie w klasie My/FrontendBundle/Entity/River.php dodajemy metodę __toString(), która zwraca wartość właściwości name.

Przykład 44.3. Kontynenty: aplikacja z panelem Sonata Wykonaj aplikację omówioną w przykładzie 34.1, wzbogacając ją o panel administracyjny Sonata. Do identyfikacji rekordów wyświetlanych na stronach akcji show użyj ciągów slug generowanych przez zachowania Doctrine. Do wykonania zadania wykorzystaj dystrybucję symfony2-customized-v6.zip.

ROZWIĄZANIE W zadaniu tym należy utworzyć dwa pliki: KontynentAdmin.php oraz PanstwoAdmin.php. W klasie KontynentAdmin konfigurujemy edycję jednej właściwości: name. W klasie PanstwoAdmin konfigurujemy natomiast dwa pola formularza edycyjnego: protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('nazwa') ->add('kontynent') ; }

Pole kontynent umożliwi edycję zależności relacyjnej. W pliku services.xml pojawią się dwa wpisy:

470

Część IX ♦ Panele administracyjne Sonata

My\FrontendBundle\Entity\Kontynent SonataAdminBundle:CRUD

MyFrontendBundle



My\FrontendBundle\Entity\Panstwo SonataAdminBundle:CRUD

MyFrontendBundle



Adresy slug włączamy tak, jak to zostało opisane w rozdziale 27. Adresy slug są automatycznie generowane na podstawie właściwości name, zatem nie występują w panelu administracyjnym.

Przykład 44.4. Filmy: aplikacja z panelem Sonata Wykonaj aplikację omówioną w przykładzie 34.2, wzbogacając ją o panel administracyjny Sonata. Do identyfikacji rekordów wyświetlanych na stronach akcji show użyj ciągów slug generowanych przez zachowania Doctrine. Do wykonania zadania wykorzystaj dystrybucję symfony2-customized-v6.zip.

Przykład 44.5. Powieści Agaty Christie: aplikacja z panelem Sonata Wykonaj aplikację omówioną w przykładzie 34.3, wzbogacając ją o panel administracyjny Sonata. Do identyfikacji rekordów wyświetlanych na stronach akcji show użyj ciągów slug generowanych przez zachowania Doctrine. Do wykonania zadania wykorzystaj dystrybucję symfony2-customized-v6.zip.

I

Dodatki

472

Część I ♦ Tworzenie prostych stron WWW

Dodatek A

Instalacja oprogramowania 1. XAMPP Do zainstalowania oprogramowania:  Apache,  PHP  i MySQL

wykorzystamy pakiet XAMPP, który jest dostępny na stronie http://www.apachefriends.org. Pobierz, a następnie uruchom program instalacyjny xampp-win32-1.7.7-VC9-installer.exe. Wszystkie opcje pozostaw domyślne. W szczególności pamiętaj, by nie zmieniać folderu przeznaczonego na oprogramowanie XAMPP, czyli c:\xampp. Po zakończeniu instalacji uruchom panel administracyjny XAMPP. Przyciski wskazane na rysunku A.1 służą do uruchamiania i zatrzymywania usług Apache oraz MySQL. Uruchom usługi Apache i MySQL, po czym odwiedź w przeglądarce adres: http://localhost

Ujrzysz wówczas okno widoczne na rysunku A.2. Podczas uruchamiania usług Apache oraz MySQL ujrzysz okno dialogowe przedstawione na rysunku A.3. Zawiera ono informację o tym, że uruchamiane usługi pozwalają na nawiązywanie połączeń sieciowych. Zezwól na nawiązywanie połączeń w sieciach lokalnych.

474

Symfony 2 od podstaw

Rysunek A.1. Panel administracyjny XAMPP i przyciski do uruchamiania i zatrzymywania usług Apache i MySQL

Rysunek A.2. Załadowanie się strony startowej pakietu XAMPP świadczy o poprawnej instalacji Jeśli zechcesz zainstalować oprogramowanie XAMPP w innym folderze, np. wewnątrz C:\Pliki programów (x86), pamiętaj o wyłączeniu ograniczeń konta użytkownika.

Dodatek A ♦ Instalacja oprogramowania

475

Rysunek A.3. Ostrzeżenie informujące o tym, że usługa Apache zezwala na nawiązywanie połączeń sieciowych

2. Modyfikacja konfiguracji PHP Konfiguracja PHP jest zapisana w pliku C:\xampp\php\php.ini. W pliku tym najpierw włącz rozszerzenie XDebug. W tym celu usuń średnik występujący na początku wiersza: [XDebug] zend_extension = "C:\xampp\php\ext\php_xdebug.dll"

Następnie włącz rozszerzenie XSL. W tym celu na końcu sekcji1 oznaczonej komentarzem: ;;;;;;;;;;;;;;;;;;;;;; ; Dynamic Extensions ; ;;;;;;;;;;;;;;;;;;;;;;

dodaj wpis: extension=php_xsl.dll

Domyślna konfiguracja XAMPP-a zawiera wpis włączający krótkie znaczniki PHP: short_open_tag = On

Pamiętaj, że jeśli zgodnie z zaleceniami Symfony 2 wyłączysz krótkie znaczniki PHP, czyli zmienisz powyższą opcję na: short_open_tag = Off

wówczas strona domowa XAMPP-a (tj. skrypty wyświetlające m.in. stronę z rysunku A.2) nie będzie działała. Oczywiście nie stanowi to żadnej przeszkody w nauce Symfony 2. Skrypty XAMPP-a zawarte w folderze C:\xampp\htdocs są zbędne i możesz je usunąć.

1

Na końcu serii wpisów extension=....

476

Symfony 2 od podstaw

3. Modyfikacja pakietu PEAR Niemal wszystkie wersje XAMPP-a zawierają błędnie skonfigurowane archiwum PEAR. Dlatego większość poleceń: pear install ...

zakończy się błędem. W celu naprawienia konfiguracji archiwum PEAR uruchom wiersz poleceń, po czym komendami: C: cd \xampp\php

przejdź do katalogu C:\xampp\php. Następnie wydaj polecenie, które utworzy nowy plik konfiguracyjny PEAR: pear config-create / C:\xampp\php\pear.ini

Następnie wydaj polecenia widoczne na listingu A.1. Listing A.1. Polecenia modyfikujące plik C:\xampp\php\pear.ini pear pear pear pear pear pear pear pear pear pear pear pear

-c -c -c -c -c -c -c -c -c -c -c -c

c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini c:\xampp\php\pear.ini

config-set config-set config-set config-set config-set config-set config-set config-set config-set config-set config-set config-set

doc_dir bin_dir ext_dir php_dir cache_dir cfg_dir data_dir download_dir php_bin temp_dir test_dir www_dir

c:\xampp\php\pear\docs c:\xampp\php c:\xampp\php\ext c:\xampp\php\pear c:\xampp\php\cache c:\xampp\php\cfg c:\xampp\php\data c:\xampp\php\download c:\xampp\php\php.exe c:\xampp\php\tmp c:\xampp\php\pear\tests c:\xampp\php\pear\www

W ten sposób w pliku C:\xampp\php\pear.ini zostaną zapisane poprawne ścieżki. Przekonasz się o tym, wydając polecenie: pear -c c:\xampp\php\pear.ini config-show

4. Uaktualnienie biblioteki PEAR Uruchom wiersz poleceń. Przejdź do folderu C:\xampp\php: C: cd \xampp\php

a następnie wydaj komendę: pear -c c:\xampp\php\pear.ini upgrade-all

W ten sposób uaktualnisz wszystkie pakiety PEAR.

Dodatek A ♦ Instalacja oprogramowania

477

5. Code Sniffer Pakiet Code Sniffer służy do kontroli standardów kodowania. Aby zainstalować pakiet Code Sniffer w folderze c:\xampp\php za pomocą wiersza poleceń, wydaj komendę: pear -c c:\xampp\php\pear.ini

install --alldeps

PHP_CodeSniffer-1.3.3

Następnie odwiedź adres: https://github.com/opensky/Symfony2-coding-standard

i pobierz plik: opensky-Symfony2-coding-standard-XXXXXX.zip

Pobrane archiwum ZIP wypakuj do folderu o nazwie Symfony2, po czym otrzymany folder przenieś do: C:\xampp\php\PEAR\PHP\CodeSniffer\Standards\Symfony2

Po wykonaniu powyższej operacji w systemie powinny pojawić się m.in. pliki: C:\xampp\php\PEAR\PHP\CodeSniffer\Standards\Symfony2\ruleset.xml C:\xampp\php\PEAR\PHP\CodeSniffer\Standards\Symfony2\Sniffs\WhiteSpace\ ´DiscourageFitzinatorSniff.php

6. phpDocumentor Najpierw odinstaluj pakiet phpDocumentor w wersji 1.4.4. W tym celu wydaj komendę: pear -c c:\xampp\php\pear.ini

uninstall PhpDocumentor-1.4.4

Następnie zainstaluj pakiet phpDocumentor w wersji 2: pear -c c:\xampp\php\pear.ini pear -c c:\xampp\php\pear.ini

channel-discover pear.phpdoc.org install --alldeps phpdoc/phpDocumentor-alpha

7. PHPUnit W celu zainstalowania oprogramowania PHPUnit wydaj komendę: pear -c c:\xampp\php\pear.ini

install pear.phpunit.de/PHPUnit

478

Symfony 2 od podstaw

8. Cygwin Instalacja pakietu Cygwin umożliwi nam korzystanie w wierszu poleceń z komend: git rsync ssh curl find

Odwiedź adres: http://www.cygwin.com

i pobierz plik: http://cygwin.com/setup.exe

Uruchom pobrany program instalacyjny. W oknie dialogowym Choose installation type/Choose a download source wybierz opcję: Install from internet

W oknie dialogowym do wyboru pakietów wyszukaj pakiet rsync. Wyszukane oprogramowanie dołącz do listy instalowanych pakietów. W tym celu kliknij w ikonę widoczną obok numeru wersji 3.0.9-1. Proces wyszukiwania i włączania instalacji pakietu rsync jest przedstawiony na rysunku A.4.

Rysunek A.4. Instalacja programu rsync

W analogiczny sposób włącz instalację pakietów openssh, curl oraz git. Procedura wyszukiwania i włączania wymienionych pakietów jest przedstawiona na rysunkach A.5, A.6 i A.7.

Dodatek A ♦ Instalacja oprogramowania

Rysunek A.5. Instalacja oprogramowania openssh

Rysunek A.6. Instalacja programu curl

Rysunek A.7. Instalacja oprogramowania git

479

480

Symfony 2 od podstaw

9. Ścieżki dostępu Przejdź do panelu sterowania. Wybierz opcję System i zabezpieczenia, a następnie System. W oknie dialogowym z rysunku A.8 wybierz opcję Zaawansowane ustawienia systemu.

Rysunek A.8. Odsyłacz do okna z zaawansowanymi ustawieniami systemu

Ujrzysz okno dialogowe widoczne na rysunku A.9. Wybierz w nim przycisk Zmienne środowiskowe. Następnie w oknie dialogowym z rysunku A.10 wybierz zmienną Path, po czym naciśnij przycisk Edytuj. Na początku wartości zmiennej dodaj ścieżki prowadzące do PHP oraz do pakietu Cygwin, czyli: C:\xampp\php;c:\cygwin\bin;

Zmodyfikowana zawartość okna edycyjnego jest przedstawiona na rysunku A.11.

Dodatek A ♦ Instalacja oprogramowania

481

Rysunek A.9. Przycisk pozwalający na modyfikację zmiennych środowiskowych

Rysunek A.10. Edycja zmiennej środowiskowej Path

Rysunek A.11. Modyfikacja zmiennej Path

W celu przetestowania poprawności modyfikacji ścieżek dostępu uruchom wiersz poleceń, i będąc w dowolnym folderze, wydaj komendę php. Następnie wprowadź skrypt: