Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00382 007855 15362596 na godz. na dobę w sumie
Symfony2. Rozbudowa frameworka - ebook/pdf
Symfony2. Rozbudowa frameworka - ebook/pdf
Autor: Liczba stron: 128
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-0297-6 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> php - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

Wiedza społeczności w pigułce. Optymalizacja, audyt oraz dostosowywanie aplikacji sieciowych opartych na systemie Symfony

Symfony2 to jeden z najpopularniejszych szkieletów do tworzenia aplikacji internetowych w języku PHP. Programiści PHP docenili jego możliwości, łatwość konfiguracji oraz elastyczność i wybierają go do najbardziej zaawansowanych projektów. Wokół tego szkieletu stworzyli również silną społeczność, która aktywnie wspiera początkujących programistów. Chcesz się przekonać, jak wykorzystać Symfony2 w codziennej pracy? Zastanawiasz się, jak rozszerzyć możliwości tego systemu i jeszcze bardziej dostosować go do własnych potrzeb? Jeżeli tak, to trafiłeś na doskonałą książkę!

Znajdziesz w niej cenne porady na temat tworzenia usług, szablonów oraz formularzy. Dowiesz się, jak zwiększyć bezpieczeństwo Twojej aplikacji za pomocą uwierzytelnienia OAuth oraz własnych adnotacji. Poznasz Doctrine oraz zobaczysz, jak udostępnić stworzone rozszerzenie innych programistom. W tej książce znajdziesz również informacje na temat automatycznego testowania stworzonego kodu oraz tworzenia dokumentacji. Jest to doskonała pozycja dla programistów chcących w pełni wykorzystać możliwości szkieletu Symfony2!

Dzięki tej książce nauczysz się: 

Rozszerz potencjał szkieletu Symfony2!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Extending Symfony 2 Web Application Framework Tłumaczenie: Łukasz Piwko ISBN: 978-83-283-0294-5 Copyright © Packt Publishing 2014. First published in the English language under the title „Extending Symfony 2 Web Application Framework”. Polish edition copyright © 2015 by Helion S.A. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. 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. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl 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/sym2rf Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/sym2rf.zip Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:258)ci O autorze O recenzentach Wst(cid:218)p Zawarto(cid:258)(cid:202) ksi(cid:200)(cid:285)ki Co jest potrzebne? Dla kogo jest ta ksi(cid:200)(cid:285)ka? Konwencje Pomoc Rozdzia(cid:239) 1. Us(cid:239)ugi i procedury nas(cid:239)uchowe Us(cid:239)ugi Us(cid:239)uga geolokalizacji Testowanie us(cid:239)ug i testowanie przy u(cid:285)yciu us(cid:239)ug Znakowanie us(cid:239)ug Procedury nas(cid:239)uchuj(cid:200)ce Aktualizowanie preferencji u(cid:285)ytkownika przy u(cid:285)yciu w(cid:239)asnych zdarze(cid:241) Poprawianie wydajno(cid:258)ci Podsumowanie Rozdzia(cid:239) 2. Polecenia i szablony Polecenia Pocz(cid:200)tkowa sytuacja Zmienianie rozmiaru obrazów u(cid:285)ytkowników Testowanie polecenia Polecenia jako interfejs do us(cid:239)ug Twig Zarz(cid:200)dzanie skryptami Testowanie rozszerzenia Twig Filtr ró(cid:285)nicy czasowej Podsumowanie 5 7 9 9 10 10 11 11 13 13 14 19 21 25 27 30 32 33 33 33 34 37 38 40 41 43 44 45 Kup książkęPoleć książkę Spis tre(cid:286)ci Rozdzia(cid:239) 3. Formularze Element wej(cid:258)ciowy dla wspó(cid:239)rz(cid:218)dnych geograficznych Podstawowa konfiguracja U(cid:285)ywanie mapy Przekszta(cid:239)canie danych Formularze wykorzystuj(cid:200)ce dane u(cid:285)ytkowników O krok dalej Pocz(cid:200)tkowa konfiguracja Dodawanie i usuwanie pól Podsumowanie Rozdzia(cid:239) 4. Bezpiecze(cid:241)stwo Uwierzytelnianie Proste uwierzytelnianie OAuth poprzez GitHub Autoryzacja Votery Adnotacje Zabezpieczanie API — przyk(cid:239)ad Podsumowanie Rozdzia(cid:239) 5. Doctrine Tworzenie w(cid:239)asnych typów danych Miejsce przebywania u(cid:285)ytkowników i miejsca spotka(cid:241) Testowanie W(cid:239)asne funkcje DQL Kontrola wersji Ustawianie wersji wszystkich jednostek U(cid:285)ywanie i aktualizowanie wersji Testowanie Tworzenie filtra Doctrine Podsumowanie Rozdzia(cid:239) 6. Udost(cid:218)pnianie w(cid:239)asnych rozszerze(cid:241) innym programistom Tworzenie pakietu Udost(cid:218)pnianie konfiguracji Przygotowanie do udost(cid:218)pnienia pakietu Badania Dokumentacja Testowanie Dystrybucja i licencjonowanie Czy to jest tylko pakiet? Podsumowanie Skorowidz 4 47 47 49 51 54 56 57 58 60 62 63 63 64 74 75 80 85 87 89 89 89 92 93 97 99 100 101 103 106 107 107 110 116 116 116 116 118 119 120 121 Kup książkęPoleć książkę 1 Us(cid:239)ugi i procedury nas(cid:239)uchowe W rozdziale tym znajduje si(cid:218) opis podstawowych wiadomo(cid:258)ci na temat systemu Symfony2. Najwa(cid:285)niejszym poj(cid:218)ciem jest us(cid:239)uga (ang. service). W istocie wi(cid:218)ksza cz(cid:218)(cid:258)(cid:202) samego systemu jest wielkim zbiorem gotowych do u(cid:285)ywania us(cid:239)ug. Na przyk(cid:239)ad po zainstalowaniu systemu mo(cid:285)na przej(cid:258)(cid:202) w konsoli do katalogu g(cid:239)ównego projektu i wpisa(cid:202) polecenie php app/console container:debug, aby wy(cid:258)wietli(cid:202) list(cid:218) wszystkich aktualnie zdefiniowanych w aplikacji us(cid:239)ug. Je(cid:258)li to zrobisz, dowiesz si(cid:218), (cid:285)e nawet jeszcze przed rozpocz(cid:218)ciem pracy masz do dyspozycji prawie 200 us(cid:239)ug. Polecenie php app/console container:debug nazwa_us(cid:239)ugi zwraca infor- macje o wybranej us(cid:239)udze; przyda si(cid:218) ono wielokrotnie w trakcie studiowania tej ksi(cid:200)(cid:285)ki. Us(cid:239)ugi Us(cid:239)uga jest konkretnym egzemplarzem jakiej(cid:258) klasy. Gdy programista u(cid:285)ywa, powiedzmy, doctri- ne, np. $this- get( doctrine ); w kontrolerze, znaczy to, (cid:285)e korzysta z us(cid:239)ugi. Ta us(cid:239)uga jest egzemplarzem klasy Doctrine EntityManager, którego nigdy nie trzeba tworzy(cid:202) samodzielnie. Kod potrzebny do jego utworzenia jest do(cid:258)(cid:202) skomplikowany, poniewa(cid:285) wymaga po(cid:239)(cid:200)czenia z baz(cid:200) danych, pewnych parametrów konfiguracyjnych itd. Gdyby ta us(cid:239)uga nie by(cid:239)a ju(cid:285) zdefiniowa- na, trzeba by tworzy(cid:202) takie egzemplarze samodzielnie. Gdyby zasz(cid:239)a konieczno(cid:258)(cid:202) zrobienia tego w ka(cid:285)dym kontrolerze, kod aplikacji sta(cid:239)by si(cid:218) zagmatwany i trudny w obs(cid:239)udze. Oto kilka z domy(cid:258)lnych us(cid:239)ug dost(cid:218)pnych w Symfony2: (cid:81) czytnik adnotacji, (cid:81) Assetic — biblioteka do zarz(cid:200)dzania zasobami, Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka (cid:81) dyspozytor zdarze(cid:241), (cid:81) fabryka wid(cid:285)etów formularza i formularzy, (cid:81) j(cid:200)dro i sk(cid:239)adnik HttpKernel Symfony2, (cid:81) monolog — biblioteka obs(cid:239)ugi dzienników, (cid:81) ruter, (cid:81) Twig — silnik szablonów. W systemie Symfony2 bardzo (cid:239)atwo tworzy si(cid:218) nowe rozszerzenia. Je(cid:258)li Twój kontroler bardzo si(cid:218) rozrós(cid:239) i trudno nad nim zapanowa(cid:202), dobrym sposobem jego poprawienia i uproszczenia jest przesuni(cid:218)cie cz(cid:218)(cid:258)ci kodu do us(cid:239)ug. Wi(cid:218)kszo(cid:258)(cid:202) us(cid:239)ug to obiekty singletonowe, czyli mog(cid:200)- ce wyst(cid:218)powa(cid:202) tylko w pojedynczym egzemplarzu. Us(cid:239)uga geolokalizacji Wyobra(cid:283) sobie aplikacj(cid:218) tworz(cid:200)c(cid:200) listy zdarze(cid:241), które nazwiemy „spotkaniami”. Kontroler umo(cid:285)liwia nam pobranie najpierw adresu IP bie(cid:285)(cid:200)cego u(cid:285)ytkownika, sprawdzenie z wykorzysta- niem tego IP lokalizacji tego u(cid:285)ytkownika oraz wy(cid:258)wietlenie spotka(cid:241) w promieniu 50 kilome- trów. Aktualnie ca(cid:239)y kod znajduje si(cid:218) w kontrolerze. Na razie jeszcze kontroler ten nie jest zbyt d(cid:239)ugi — zawiera jedn(cid:200) metod(cid:218) i ca(cid:239)a klasa zajmuje jakie(cid:258) 50 wierszy kodu. Ale z czasem dodamy wi(cid:218)cej kodu, aby na przyk(cid:239)ad móc wy(cid:258)wietla(cid:202) tylko ulubione spotkania u(cid:285)ytkownika albo takie, w których u(cid:285)ytkownik bra(cid:239) udzia(cid:239) najcz(cid:218)(cid:258)ciej. Gdy po(cid:239)(cid:200)czy si(cid:218) te wszystkie infor- macje i doda skomplikowane obliczenia maj(cid:200)ce na celu znalezienie najodpowiedniejszych spotka(cid:241) dla danego u(cid:285)ytkownika, kod mo(cid:285)e rozrosn(cid:200)(cid:202) si(cid:218) do niebotycznych rozmiarów! Ten prosty problem mo(cid:285)na rozwi(cid:200)za(cid:202) na kilka sposobów. Logik(cid:218) geokodowania mo(cid:285)na na razie przenie(cid:258)(cid:202) do osobnej metody. B(cid:218)dzie to dobre tymczasowe posuni(cid:218)cie, ale lepiej my(cid:258)le(cid:202) przysz(cid:239)o- (cid:258)ciowo i przenie(cid:258)(cid:202) cz(cid:218)(cid:258)(cid:202) logiki do us(cid:239)ug, do których nale(cid:285)y. Aktualnie nasz kod wygl(cid:200)da tak: use Geocoder\HttpAdapter\CurlHttpAdapter; use Geocoder\Geocoder; use Geocoder\Provider\FreeGeoIpProvider; public function indexAction() { Narz(cid:218)dzia do geokodowania (oparte na doskona(cid:239)ej bibliotece geokodowania — http://geocoder- php.org/) zainicjujemy przy u(cid:285)yciu nast(cid:218)puj(cid:200)cego kodu: $adapter = new CurlHttpAdapter(); $geocoder = new Geocoder(); $geocoder- registerProviders(array( new FreeGeoIpProvider($adapter), )); 14 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe Pobieramy adres IP u(cid:285)ytkownika: $ip = $this- get( request )- getClientIp(); // Mo(cid:298)na te(cid:298) u(cid:298)y(cid:252) domy(cid:286)lnego. if ($ip == 127.0.0.1 ) { $ip = 114.247.144.250 ; } Pobieramy wspó(cid:239)rz(cid:218)dne i dostosowujemy je przy u(cid:285)yciu poni(cid:285)szego kodu, aby tworzy(cid:239)y mniej wi(cid:218)cej kwadrat o boku 50 km: $result = $geocoder- geocode($ip); $lat = $result- getLatitude(); $long = $result- getLongitude(); $lat_max = $lat + 0.25; // oko(cid:225)o 25 km $lat_min = $lat - 0.25; $long_max = $long + 0.3; // oko(cid:225)o 25 km $long_min = $long - 0.3; Na podstawie tych wszystkich informacji tworzymy zapytanie: $em = $this- getDoctrine()- getManager(); $qb = $em- createQueryBuilder(); $qb- select( e ) - from( KhepinBookBundle:Meetup , e ) - where( e.latitude :lat_max ) - andWhere( e.latitude :lat_min ) - andWhere( e.longitude :long_max ) - andWhere( e.longitude :long_min ) - setParameters([ lat_max = $lat_max, lat_min = $lat_min, long_max = $long_max, long_min = $long_min ]); Pobieramy wyniki i przekazujemy je do szablonu: $meetups = $qb- getQuery()- execute(); return [ ip = $ip, result = $result, meetups = $meetups]; } Chcemy si(cid:218) pozby(cid:202) inicjacji geokodowania. Najlepiej, (cid:285)eby wszystko to odbywa(cid:239)o si(cid:218) automatycz- nie, a dost(cid:218)p do geokodera odbywa(cid:239) si(cid:218) za pomoc(cid:200) instrukcji $this- get( geocoder );. Sk(cid:200)d pobra(cid:202) przyk(cid:239)ady kodu? Pliki z przyk(cid:239)adami kodu (cid:283)ród(cid:239)owego mo(cid:285)na pobra(cid:202) z serwera FTP wydawnictwa Helion pod adresem ftp://ftp.helion.pl/przyklady/sym2rf.zip. 15 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka Us(cid:239)ugi mo(cid:285)na definiowa(cid:202) bezpo(cid:258)rednio w pliku config.yml systemu Symfony pod kluczem services, jak pokazano poni(cid:285)ej: services: geocoder: class: Geocoder\Geocoder To wszystko! Zdefiniowali(cid:258)my us(cid:239)ug(cid:218), która jest teraz dost(cid:218)pna we wszystkich naszych kontrole- rach. Teraz nasz kod wygl(cid:200)da tak: // Tworzy klas(cid:266) geokodowania. $adapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); $geocoder = $this- get( geocoder ); $geocoder- registerProviders(array( new \Geocoder\Provider\FreeGeoIpProvider($adapter), )); Ju(cid:285) widz(cid:218), jak przewracasz oczami i stwierdzasz, (cid:285)e to niewiele pomaga. Jest tak, poniewa(cid:285) inicjacja geokodera jest nieco bardziej skomplikowana ni(cid:285) zwyk(cid:239)e wywo(cid:239)anie new \Geocoder\ Geocoder(). Konieczne jest utworzenie obiektu innej klasy i przekazanie go jako parametru do metody. Dobra wiadomo(cid:258)(cid:202) jest taka, (cid:285)e wszystko to mo(cid:285)na zrobi(cid:202) w definicji us(cid:239)ugi. Wystarczy tylko j(cid:200) zmodyfikowa(cid:202) w nast(cid:218)puj(cid:200)cy sposób: services: # Definiuje klas(cid:266) adaptacyjn(cid:261). geocoder_adapter: class: Geocoder\HttpAdapter\CurlHttpAdapter public: false # Definiuje klas(cid:266) dostawcz(cid:261). geocoder_provider: class: Geocoder\Provider\FreeGeoIpProvider public: false # Klasie dostawczej jest przekazywany adapter jako argument. arguments: [@geocoder_adapter] geocoder: class: Geocoder\Geocoder # Po inicjacji wywo(cid:225)ujemy na geokoderze metod(cid:266), aby ustawi(cid:252) odpowiednie parametry. calls: - [registerProviders, [[@geocoder_provider]]] Ten kod jest ju(cid:285) troch(cid:218) d(cid:239)u(cid:285)szy, ale to jest jedyne miejsce, w którym musimy go napisa(cid:202). Warto zwróci(cid:202) uwag(cid:218) na par(cid:218) rzeczy: (cid:81) W rzeczywisto(cid:258)ci zdefiniowali(cid:258)my trzy us(cid:239)ugi, poniewa(cid:285) nasz geokoder wymaga egzemplarzy dwóch innych klas. (cid:81) Aby przekaza(cid:202) referencj(cid:218) do us(cid:239)ugi jako argument do innej us(cid:239)ugi, u(cid:285)yli(cid:258)my sk(cid:239)adni @+nazwa_us(cid:239)ugi. 16 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe (cid:81) Nie musimy si(cid:218) ogranicza(cid:202) tylko do definicji new Class($argument);. Mo(cid:285)emy te(cid:285) wywo(cid:239)a(cid:202) metod(cid:218) na klasie po utworzeniu jej egzemplarza. Istnieje nawet mo(cid:285)liwo(cid:258)(cid:202) bezpo(cid:258)redniego ustawiania w(cid:239)a(cid:258)ciwo(cid:258)ci, je(cid:258)li s(cid:200) publiczne. (cid:81) Dwie pierwsze us(cid:239)ugi oznaczyli(cid:258)my jako prywatne, co znaczy, (cid:285)e nie b(cid:218)d(cid:200) dost(cid:218)pne w kontrolerach. Mog(cid:200) natomiast by(cid:202) wstrzykiwane przez kontener wstrzykiwania zale(cid:285)no(cid:258)ci (ang. dependency injection container — DIC) do innych us(cid:239)ug. Teraz nasz kod wygl(cid:200)da tak: // Pobiera adres IP u(cid:298)ytkownika. $ip = $this- get( request )- getClientIp(); // Albo u(cid:298)ywa domy(cid:286)lnego. if ($ip == 127.0.0.1 ) { $ip = 114.247.144.250 ; } // Sprawdza wspó(cid:225)rz(cid:266)dne u(cid:298)ytkownika. $result = $this- get( geocoder )- geocode($ip); $lat = $result- getLatitude(); // ... Reszta kodu pozostaje bez zmian. W tym przypadku kontrolery rozszerzaj(cid:200) klas(cid:218) BaseController, która ma dost(cid:218)p do DIC, poniewa(cid:285) implementuje interfejs ContainerAware. Wszystkie wywo(cid:239)ania $this- get( nazwa_us(cid:239)ugi ) s(cid:200) przekazywane kontenerowi, który konstruuje (w razie potrzeby) i zwraca us(cid:239)ug(cid:218). Posuniemy si(cid:218) jeszcze dalej i zdefiniujemy w(cid:239)asn(cid:200) klas(cid:218), która bezpo(cid:258)rednio b(cid:218)dzie pobiera(cid:202) adres IP u(cid:285)ytkownika oraz zwraca(cid:202) tablic(cid:218) maksymalnych i minimalnych d(cid:239)ugo(cid:258)ci i szeroko(cid:258)ci geograficznych. Utworzymy nast(cid:218)puj(cid:200)c(cid:200) klas(cid:218): namespace Khepin\BookBundle\Geo; use Geocoder\Geocoder; use Symfony\Component\HttpFoundation\Request; class UserLocator { protected $geocoder; protected $user_ip; public function __construct(Geocoder $geocoder, Request $request) { $this- geocoder = $geocoder; $this- user_ip = $request- getClientIp(); if ($this- user_ip == 127.0.0.1 ) { $this- user_ip = 114.247.144.250 ; } 17 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka } public function getUserGeoBoundaries($precision = 0.3) { // Sprawdza wspó(cid:225)rz(cid:266)dne u(cid:298)ytkownika. $result = $this- geocoder- geocode($this- user_ip); $lat = $result- getLatitude(); $long = $result- getLongitude(); $lat_max = $lat + 0.25; // oko(cid:225)o 25 km $lat_min = $lat - 0.25; $long_max = $long + 0.3; // oko(cid:225)o 25 km $long_min = $long - 0.3; return [ lat_max = $lat_max, lat_min = $lat_min, long_max = $long_max, long_min = $long_min]; } } Konstruktor tej klasy przyjmuje jako argumenty zmienne geocoder i request, a nast(cid:218)pnie klasa ta wykonuje ca(cid:239)(cid:200) prac(cid:218), któr(cid:200) na pocz(cid:200)tku wykonywali(cid:258)my w kontrolerze. Podobnie jak wcze(cid:258)niej, klas(cid:218) t(cid:218) zdefiniujemy jako us(cid:239)ug(cid:218), aby by(cid:239)a (cid:239)atwo dost(cid:218)pna w kontrolerach: # config.yml services: #... user_locator: class: Khepin\BookBundle\Geo\UserLocator scope: request arguments: [@geocoder, @request] Zwró(cid:202) uwag(cid:218) na definicj(cid:218) zakresu w tym kodzie. DIC ma domy(cid:258)lnie dwa zakresy: container i prototype, do których system dodaje jeszcze trzeci, o nazwie request. W poni(cid:285)szej tabeli znaj- duje si(cid:218) opis ró(cid:285)nic mi(cid:218)dzy nimi. Zakres Ró(cid:285)nice container prototype request Wszystkie wywo(cid:239)ania $this- get( service_name ) zwracaj(cid:200) ten sam egzemplarz us(cid:239)ugi. Wszystkie wywo(cid:239)ania $this- get( service_name ) zwracaj(cid:200) nowy egzemplarz us(cid:239)ugi. Wszystkie wywo(cid:239)ania $this- get( service_name ) zwracaj(cid:200) ten sam egzemplarz us(cid:239)ugi w (cid:285)(cid:200)daniu. Symfony mo(cid:285)e mie(cid:202) (cid:285)(cid:200)dania podrz(cid:218)dne (np. zawieraj(cid:200)ce kontroler w Twig). Z wykonanych dzia(cid:239)a(cid:241) odnie(cid:258)li(cid:258)my tak(cid:200) korzy(cid:258)(cid:202), (cid:285)e us(cid:239)uga samodzielnie zdobywa wszystkie potrzebne jej informacje, ale niestety staje si(cid:218) bezu(cid:285)yteczna w kontekstach, w których nie ma (cid:285)(cid:200)da(cid:241). Gdyby(cid:258)my chcieli utworzy(cid:202) polecenie pobieraj(cid:200)ce wszystkie adresy IP, z którymi (cid:239)(cid:200)czy(cid:239) si(cid:218) u(cid:285)ytkownik, i wysy(cid:239)aj(cid:200)ce mu wiadomo(cid:258)ci o spotkaniach odbywaj(cid:200)cych si(cid:218) w weekend w jego okolicy, to ten projekt uniemo(cid:285)liwi(cid:239)by nam u(cid:285)ycie potrzebnej do tego klasy Khepin\BookBundle\ Geo\UserLocator. 18 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe Jak wida(cid:202), domy(cid:258)lnie us(cid:239)ugi znajduj(cid:200) si(cid:218) w zakresie kontenera, co znaczy, (cid:285)e ich egzemplarz jest two- rzony tylko raz, a potem wielokrotnie u(cid:285)ywany zgodnie z zasadami wzorca projektowego Singleton. Ponadto nale(cid:285)y zauwa(cid:285)y(cid:202), (cid:285)e DIC nie tworzy wszystkich us(cid:239)ug natychmiast, tylko na (cid:285)(cid:200)danie. Je(cid:258)li kod znajduj(cid:200)cy si(cid:218) w innym kontrolerze nie u(cid:285)ywa us(cid:239)ugi user_locator, to ani ta us(cid:239)uga, ani (cid:285)adna z us(cid:239)ug, od których zale(cid:285)y (geocoder, geocoder_provider i geocoder_adapter), nie zostanie utworzona. Ponadto nale(cid:285)y pami(cid:218)ta(cid:202), (cid:285)e konfiguracja zapisana w pliku config.yml jest buforowana w (cid:258)rodowisku produkcyjnym, dzi(cid:218)ki czemu definicja tych us(cid:239)ug powoduje minimalny lub wr(cid:218)cz zerowy narzut. Teraz nasz kontroler jest ju(cid:285) znacznie prostszy i wygl(cid:200)da nast(cid:218)puj(cid:200)co: $boundaries = $this- get( user_locator )- getUserGeoBoundaries(); // Tworzy zapytanie do bazy danych. $em = $this- getDoctrine()- getManager(); $qb = $em- createQueryBuilder(); $qb- select( e ) - from( KhepinBookBundle:Meetup , e ) - where( e.latitude :lat_max ) - andWhere( e.latitude :lat_min ) - andWhere( e.longitude :long_max ) - andWhere( e.longitude :long_min ) - setParameters($boundaries); // Pobiera informacje o interesuj(cid:261)cych spotkaniach. $meetups = $qb- getQuery()- execute(); return [ meetups = $meetups]; Najwi(cid:218)cej miejsca zajmuje zapytanie Doctrine, które (cid:239)atwo mo(cid:285)na przenie(cid:258)(cid:202) do klasy repozyto- rium, aby jeszcze bardziej upro(cid:258)ci(cid:202) kontroler. Jak wida(cid:202) na przedstawionym przyk(cid:239)adzie, definiowanie i tworzenie us(cid:239)ug w Symfony2 jest do(cid:258)(cid:202) (cid:239)atwe i niezbyt kosztowne. Utworzyli(cid:258)my w(cid:239)asn(cid:200) klas(cid:218) UserLocator, zamienili(cid:258)my j(cid:200) w us(cid:239)ug(cid:218) oraz dowiedzieli(cid:258)my si(cid:218), (cid:285)e mo(cid:285)e ona zale(cid:285)e(cid:202) od innych naszych us(cid:239)ug, np. @geocoder. Nie sko(cid:241)czyli- (cid:258)my jeszcze z us(cid:239)ugami ani DIC, poniewa(cid:285) s(cid:200) to podstawowe sk(cid:239)adniki prawie wszystkich technik zwi(cid:200)zanych z rozszerzaniem systemu Symfony2. B(cid:218)dzie o nich mowa jeszcze wiele razy w tej ksi(cid:200)(cid:285)ce i dlatego zanim przejdziemy dalej, koniecznie musimy je dobrze zrozumie(cid:202). Testowanie us(cid:239)ug i testowanie przy u(cid:285)yciu us(cid:239)ug Jedn(cid:200) z wielkich zalet umieszczania kodu w us(cid:239)ugach jest to, (cid:285)e us(cid:239)ugi s(cid:200) po prostu klasami PHP. Dzi(cid:218)ki temu mo(cid:285)na je szybko testowa(cid:202). Nie trzeba do tego kontrolera ani DIC. Wystarczy tylko utworzy(cid:202) atrapy klas geocoder i request. W folderze test pakietu mo(cid:285)na utworzy(cid:202) folder o nazwie Geo, w którym b(cid:218)dziemy testowa(cid:202) nasz(cid:200) klas(cid:218) UserLocator. Jako (cid:285)e testowana b(cid:218)dzie zwyk(cid:239)a klasa PHP, nie trzeba u(cid:285)ywa(cid:202) klasy WebTestCase. Wystarczy nam standardowa klasa PHPUnit_Framework_TestCase. Nasza klasa zawiera 19 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka tylko jedn(cid:200) metod(cid:218) geokoduj(cid:200)c(cid:200) adres IP i zwracaj(cid:200)c(cid:200) zbiór wspó(cid:239)rz(cid:218)dnych okre(cid:258)lonych z wyzna- czon(cid:200) precyzj(cid:200). Mo(cid:285)emy imitowa(cid:202) dzia(cid:239)anie geokodera przez zwracanie na sztywno ustawio- nych liczb, dzi(cid:218)ki czemu nie b(cid:218)dziemy musieli wykonywa(cid:202) wywo(cid:239)a(cid:241) sieciowych, które spo- wolni(cid:239)yby nasze testy. Poni(cid:285)ej znajduje si(cid:218) prosty przypadek testowy: class UserLocatorTest extends PHPUnit_Framework_TestCase { public function testGetBoundaries() { $geocoder = $this- getMock( Geocoder\Geocoder ); $result = $this- getMock( Geocoder\Result\Geocoded ); $geocoder- expects($this- any())- method( geocode )- will($this- returnValue($result)); $result- expects($this- any())- method( getLatitude )- will($this- returnValue(3)); $result- expects($this- any())- method( getLongitude ) - will($this- returnValue(7)); $request = $this- getMock ( Symfony\Component\HttpFoundation\Request , [ getUserIp ]); $locator = new UserLocator($geocoder, $request); $boundaries = $locator- getUserGeoBoundaries(0); $this- assertTrue($boundaries[ lat_min ] == 3); } } Teraz mo(cid:285)emy sprawdzi(cid:202), czy dzia(cid:239)a nasza klasa, ale co z reszt(cid:200) logiki kontrolera? Dla kontrolera mo(cid:285)emy napisa(cid:202) prosty test integracyjny, aby sprawdzi(cid:202), czy na wyrenderowanej stronie znajduj(cid:200) si(cid:218) informacje o jakich(cid:258) spotkaniach. Ale w niektórych przypadkach podczas testowania lepiej jest nie wywo(cid:239)ywa(cid:202) zewn(cid:218)trznych us(cid:239)ug ze wzgl(cid:218)du na wydajno(cid:258)(cid:202), wygod(cid:218) lub po prostu brak takiej mo(cid:285)liwo(cid:258)ci. W takiej sytuacji równie(cid:285) mo(cid:285)na pos(cid:239)u(cid:285)y(cid:202) si(cid:218) atrapami us(cid:239)ug, które b(cid:218)d(cid:200) u(cid:285)ywane w kontrolerze. W naszych testach musimy to zrobi(cid:202) tak: public function testIndexMock() { $client = static::createClient(); $locator = $this- getMockBuilder ( Khepin\BookBundle\Geo\UserLocator ) - disableOriginalConstructor()- getMock(); $boundaries = [ lat_max = 40.2289, lat_min = 39.6289, long_max = 116.6883, long_min = 116.0883]; $locator- expects($this- any())- method ( getUserGeoBoundaries )- will($this- returnValue($boundaries)); 20 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe $client- getContainer()- set( user_locator , $locator); $crawler = $client- request( GET , / ); // Sprawdza, czy strona zawiera oczekiwane informacje o spotkaniach. } W kodzie tym utworzyli(cid:258)my atrap(cid:218) klasy UserLocator, która zawsze zwraca te same wspó(cid:239)rz(cid:218)dne. Dzi(cid:218)ki temu mamy wi(cid:218)ksz(cid:200) kontrol(cid:218) nad tym, co testujemy, i nie musimy d(cid:239)ugo czeka(cid:202) na wywo- (cid:239)anie serwera geolokacyjnego. Znakowanie us(cid:239)ug Zapewne podczas u(cid:285)ywania systemu Symfony spotka(cid:239)e(cid:258) si(cid:218) ju(cid:285) z oznakowanymi us(cid:239)ugami, np. przy definiowaniu w(cid:239)asnych wid(cid:285)etów formularza albo voterów zabezpiecze(cid:241). Oznakowanymi us(cid:239)u- gami s(cid:200) te(cid:285) procedury nas(cid:239)uchu zdarze(cid:241), o których b(cid:218)dzie mowa w drugiej cz(cid:218)(cid:258)ci tego rozdzia(cid:239)u. W poprzednich przyk(cid:239)adach utworzyli(cid:258)my us(cid:239)ug(cid:218) user_locator, której dzia(cid:239)anie zale(cid:285)y od us(cid:239)ugi geokodowania. Ale u(cid:285)ytkownika mo(cid:285)na zlokalizowa(cid:202) na wiele sposobów. Mo(cid:285)na pos(cid:239)u(cid:285)y(cid:202) si(cid:218) danymi adresowymi z profilu, co jest szybsz(cid:200) i dok(cid:239)adniejsz(cid:200) metod(cid:200) ni(cid:285) sprawdzanie wed(cid:239)ug adresu IP. Mo(cid:285)na te(cid:285) u(cid:285)y(cid:202) ró(cid:285)nych dostawców internetowych, takich jak FreeGeoIp, co zro- bili(cid:258)my w poprzednim kodzie, albo utrzymywa(cid:202) lokaln(cid:200) baz(cid:218) danych geoip. Mo(cid:285)na nawet wszyst- kie te techniki zaimplementowa(cid:202) w jednej aplikacji i wypróbowywa(cid:202) je jedn(cid:200) po drugiej, za- czynaj(cid:200)c od najbardziej dok(cid:239)adnej. Interfejs dla tego nowego typu geokodera zdefiniujemy nast(cid:218)puj(cid:200)co: namespace Khepin\BookBundle\Geo; interface Geocoder { public function getAccuracy(); public function geocode($ip); } Nast(cid:218)pnie zdefiniujemy dwa geokodery przy u(cid:285)yciu poni(cid:285)szego kodu. Pierwszy z nich opakowuje istniej(cid:200)cy geokoder w now(cid:200) klas(cid:218) implementuj(cid:200)c(cid:200) nasz interfejs Geocoder: namespace Khepin\BookBundle\Geo; use Geocoder\Geocoder as IpGeocoder; class FreeGeoIpGeocoder implements Geocoder { public function __construct(IpGeocoder $geocoder) { $this- geocoder = $geocoder; } 21 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka public function geocode($ip) { return $this- geocoder- geocode($ip); } public function getAccuracy() { return 100; } } Pierwszy typ geokodera jest skonfigurowany nast(cid:218)puj(cid:200)co: freegeoip_geocoder: class: Khepin\BookBundle\Geo\FreeGeoIpGeocoder arguments: [@geocoder] Drugi geokoder za ka(cid:285)dym razem zwraca losow(cid:200) lokalizacj(cid:218): namespace Khepin\BookBundle\Geo; class RandomLocationGeocoder implements Geocoder { public function geocode($ip) { return new Result(); } public function getAccuracy() { return 0; } } class Result { public function getLatitude() { return rand(-85, 85); } public function getLongitude() { return rand(-180, 180); } public function getCountryCode() { return CN ; } } 22 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe Konfiguracja drugiego geokodera wygl(cid:200)da tak: random_geocoder: class: Khepin\BookBundle\Geo\RandomLocationGeocoder Je(cid:258)li zmienimy konfiguracj(cid:218) naszej us(cid:239)ugi user_locator tak, aby przestawi(cid:202) j(cid:200) na u(cid:285)ywanie jedne- go z tych geokoderów, wszystko nam zadzia(cid:239)a. Ale my chcemy, aby nasza us(cid:239)uga bez (cid:285)adnych zmian w jej kodzie mog(cid:239)a u(cid:285)ywa(cid:202) wszystkich dost(cid:218)pnych metod oraz wybra(cid:202) najbardziej pre- cyzyjn(cid:200) z nich, nawet gdy zostan(cid:200) dodane nowe. Oznaczymy nasze us(cid:239)ugi przez dodanie znaczników w ich konfiguracjach: freegeoip_geocoder: class: Khepin\BookBundle\Geo\FreeGeoIpGeocoder arguments: [@geocoder] tags: - { name: khepin_book.geocoder } random_geocoder: class: Khepin\BookBundle\Geo\RandomLocationGeocoder tags: - { name: khepin_book.geocoder } Nie mo(cid:285)emy ich wszystkich przekaza(cid:202) bezpo(cid:258)rednio w konstruktorze klasy, wi(cid:218)c dodamy do klasy UserLocator metod(cid:218) addGeocoder: class UserLocator { protected $geocoders = []; protected $user_ip; // St(cid:261)d usuni(cid:266)to geokoder. public function __construct(Request $request) { $this- user_ip = $request- getClientIp(); } public function addGeocoder(Geocoder $geocoder) { $this- geocoders[] = $geocoder; } // Wybiera najodpowiedniejszy geokoder. public function getBestGeocoder(){/* ... */} // ... } Nie mo(cid:285)na poinformowa(cid:202) DIC o ch(cid:218)ci dodania oznakowanych us(cid:239)ug tylko przez konfiguracj(cid:218). Robi si(cid:218) to w czasie dzia(cid:239)ania kompilatora — podczas kompilacji DIC. 23 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka W przebiegach kompilatora mo(cid:285)na dynamicznie modyfikowa(cid:202) definicje us(cid:239)ug. Mo(cid:285)na to wy- korzysta(cid:202) dla us(cid:239)ug oznakowanych oraz do tworzenia pakietów w(cid:239)(cid:200)czaj(cid:200)cych dodatkowe funkcje, gdy jaki(cid:258) inny pakiet równie(cid:285) jest obecny i skonfigurowany. Oto przyk(cid:239)ad wykorzystania przebiegu kompilatora: namespace Khepin\BookBundle\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler \CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class UserLocatorPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { if (!$container- hasDefinition( khepin_book.user_locator )) { return; } $service_definition = $container- getDefinition ( khepin_book.user_locator ); $tagged = $container- findTaggedServiceIds ( khepin_book.geocoder ); foreach ($tagged as $id = $attrs) { $service_definition- addMethodCall( addGeocoder , [new Reference($id)] ); } } } Po potwierdzeniu, (cid:285)e us(cid:239)uga user_locator (tu przemianowana na khepin_book.user_locator) istnieje, wyszukujemy wszystkie us(cid:239)ugi z odpowiednim znacznikiem i modyfikujemy definicj(cid:218) us(cid:239)ugi khepin_book.user_locator w taki sposób, aby je (cid:239)adowa(cid:239)a. Mo(cid:285)na zdefiniowa(cid:202) atrybuty znacznika. Dzi(cid:218)ki temu mogliby(cid:258)my na przyk(cid:239)ad zapisa(cid:202) dok(cid:239)adno(cid:258)(cid:202) ka(cid:285)dego geokodera w jego konfiguracji, a nast(cid:218)pnie w przebiegu kompilatora lokalizatorowi u(cid:285)ytkownika do- starczy(cid:202) najprecyzyjniejszy dekoder: tags: - { name: khepin_book.geocoder, accuracy: 69 } 24 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe Gdy programista zdefiniuje konfiguracj(cid:218) YAML dla us(cid:239)ug, Symfony na podstawie tych infor- macji wewn(cid:218)trznie tworzy definicje us(cid:239)ug. Dzi(cid:218)ki dodaniu przebiegu kompilatora mo(cid:285)emy mody- fikowa(cid:202) te definicje w sposób dynamiczny. Definicje us(cid:239)ug s(cid:200) nast(cid:218)pnie buforowane, aby nie trzeba by(cid:239)o ponownie kompilowa(cid:202) kontenera. Procedury nas(cid:239)uchuj(cid:200)ce Procedury nas(cid:239)uchuj(cid:200)ce realizuj(cid:200) implementacj(cid:218) wzorca projektowego Obserwator. We wzorcu tym wybrany fragment kodu nie próbuje rozpocz(cid:200)(cid:202) wykonywania ca(cid:239)ego kodu, który powi- nien zosta(cid:202) wykonany w danym momencie. Zamiast tego powiadamia swoich obserwatorów, (cid:285)e doszed(cid:239) do pewnego punktu wykonywania, i mog(cid:200) oni przej(cid:200)(cid:202) kontrol(cid:218), je(cid:258)li jest taka potrzeba. W Symfony wzorzec Obserwator jest realizowany przez zdarzenia. Ka(cid:285)da klasa i funkcja mo(cid:285)e wyzwoli(cid:202) zdarzenie, gdy tylko uzna to za stosowne. Samo zdarzenie mo(cid:285)e by(cid:202) zdefiniowane w klasie. Dzi(cid:218)ki temu mo(cid:285)na przekaza(cid:202) wi(cid:218)cej informacji do obserwuj(cid:200)cego je kodu. System tak(cid:285)e zg(cid:239)asza zdarzenia w ró(cid:285)nych momentach obs(cid:239)ugi (cid:285)(cid:200)da(cid:241). S(cid:200) to: (cid:81) kernel.request — to zdarzenie ma miejsce przed dotarciem do kontrolera. Jest u(cid:285)ywane wewn(cid:218)trznie do zape(cid:239)niania danymi obiektu request. (cid:81) kernel.controller — to zdarzenie ma miejsce bezpo(cid:258)rednio przed uruchomieniem kontrolera. Mo(cid:285)na je wykorzysta(cid:202) w celu zmiany kontrolera, który jest aktualnie wykonywany. (cid:81) kernel.view — to zdarzenie ma miejsce po wykonaniu kontrolera, je(cid:258)li kontroler ten nie zwróci(cid:239) obiektu response. Mo(cid:285)na je wykorzysta(cid:202) do zlecenia domy(cid:258)lnej obs(cid:239)ugi renderowania widoku przez Twig. (cid:81) kernel.response — to zdarzenie ma miejsce przed wys(cid:239)aniem odpowiedzi. Mo(cid:285)na je wykorzysta(cid:202) do zmodyfikowania odpowiedzi przed jej wys(cid:239)aniem. (cid:81) kernel.terminate — to zdarzenie ma miejsce po wys(cid:239)aniu odpowiedzi. Mo(cid:285)na je wykorzysta(cid:202) do wykonania czasoch(cid:239)onnych operacji, które nie musz(cid:200) generowa(cid:202) odpowiedzi. (cid:81) kernel.exception — to zdarzenie ma miejsce, gdy system przechwyci nieobs(cid:239)u(cid:285)ony wyj(cid:200)tek. Doctrine tak(cid:285)e zg(cid:239)asza zdarzenia w czasie cyklu istnienia obiektu (np. przed zapisaniem lub po zapisaniu go w bazie danych), ale to ca(cid:239)kiem osobny temat. Wszystko na temat zdarze(cid:241) cyklu istnienia obiektów Doctrine mo(cid:285)na znale(cid:283)(cid:202) na stronie http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#reference- events-lifecycle-events. 25 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka Zdarzenia s(cid:200) niezwykle przydatne i dlatego b(cid:218)d(cid:200) u(cid:285)ywane jeszcze wiele razy w ró(cid:285)nych miej- scach tej ksi(cid:200)(cid:285)ki. Gdy udost(cid:218)pnia si(cid:218) rozszerzenia do Symfony innym programistom, zawsze dobrym pomys(cid:239)em jest zdefiniowanie i wyzwalanie w(cid:239)asnych zdarze(cid:241), które mog(cid:200) s(cid:239)u(cid:285)y(cid:202) jako w(cid:239)asno(cid:258)ciowe punkty rozszerze(cid:241). Teraz rozbudujemy przyk(cid:239)ad z poprzedniej cz(cid:218)(cid:258)ci rozdzia(cid:239)u, aby zobaczy(cid:202), do czego mog(cid:200) przyda(cid:202) si(cid:218) procedury nas(cid:239)uchuj(cid:200)ce. W pierwszej cz(cid:218)(cid:258)ci zbudowali(cid:258)my stron(cid:218) internetow(cid:200) wy(cid:258)wietlaj(cid:200)c(cid:200) informacje o spotkaniach w okolicy miejsca przebywania u(cid:285)ytkownika. Teraz dodatkowo sprawimy, (cid:285)e informacje te b(cid:218)d(cid:200) filtrowane zgodnie z preferencjami u(cid:285)ytkownika. Aktualizujemy schemat, aby utworzy(cid:202) relacj(cid:218) „wiele do wielu” mi(cid:218)dzy u(cid:285)ytkownikami i spo- tkaniami: // Entity/User.php /** * @ORM\ManyToMany(targetEntity= Meetup , mappedBy= attendees ) */ protected $meetups; // Entity/Meetup.php /** * @ORM\ManyToMany(targetEntity= User , inversedBy= meetups ) */ protected $attendees; W kontrolerze mamy prost(cid:200) akcj(cid:218) pozwalaj(cid:200)c(cid:200) wzi(cid:200)(cid:202) udzia(cid:239) w spotkaniu: /** * @Route( /meetups/{meetup_id}/join ) * @Template() */ public function joinAction($meetup_id) { $em = $this- getDoctrine()- getManager(); $meetup = $em- getRepository( KhepinBookBundle:Meetup ) - find($meetup_id); $form = $this- createForm( new JoinMeetupType(), $meetup, [ action = , method = POST ] ); $form- add( submit , submit , array( label = Join )); $form- handleRequest($this- get( request )); $user = $this- get( security.context )- getToken()- getUser(); if ($form- isValid()) { $meetup- addAttendee($user); 26 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe $em- flush(); } $form = $form- createView(); return [ meetup = $meetup, user = $user, form = $form]; } U(cid:285)yli(cid:258)my formularza, mimo (cid:285)e ta akcja jest bardzo prosta, poniewa(cid:285) przesy(cid:239)anie wszystkich informacji w adresie URL w celu zaktualizowania bazy danych i zarejestrowania u(cid:285)ytkownika jako uczestnika by(cid:239)o- by s(cid:239)abym punktem, nara(cid:285)aj(cid:200)cym aplikacj(cid:218) na wiele ataków, np. CSRF. Aktualizowanie preferencji u(cid:285)ytkownika przy u(cid:285)yciu w(cid:239)asnych zdarze(cid:241) Chcemy napisa(cid:202) kod generuj(cid:200)cy now(cid:200) list(cid:218) ulubionych spotka(cid:241) u(cid:285)ytkownika. W tym celu musimy zmieni(cid:202) logik(cid:218) wy(cid:258)wietlania strony g(cid:239)ównej. B(cid:218)dziemy wy(cid:258)wietla(cid:202) nie tylko list(cid:218) spotka(cid:241) z pobli(cid:285)a miejsca przebywania u(cid:285)ytkownika, ale dodatkowo przefiltrujemy dane wed(cid:239)ug pre- ferencji tego u(cid:285)ytkownika. Przewidujemy, (cid:285)e strona g(cid:239)ówna naszej aplikacji b(cid:218)dzie cz(cid:218)sto wy- (cid:258)wietlana, przez co wykonywanie wszystkich oblicze(cid:241) przy ka(cid:285)dym jej otwarciu mo(cid:285)e by(cid:202) bardzo kosztowne. Dlatego lepiej b(cid:218)dzie utworzy(cid:202) gotow(cid:200) list(cid:218) ulubionych rodzajów spotka(cid:241), któr(cid:200) b(cid:218)dziemy modyfikowa(cid:202), gdy u(cid:285)ytkownik zapisze si(cid:218) na jakie(cid:258) spotkanie lub zrezygnuje z udzia(cid:239)u w jakim(cid:258) spotkaniu. W przysz(cid:239)o(cid:258)ci mo(cid:285)na te(cid:285) list(cid:218) t(cid:218) aktualizowa(cid:202) na podstawie przegl(cid:200)- danych stron, nawet je(cid:258)li u(cid:285)ytkownik nie zapisze si(cid:218) na dane spotkanie. Teraz musimy zastanowi(cid:202) si(cid:218), gdzie umie(cid:258)ci(cid:202) nasz kod. Narzuca si(cid:218) my(cid:258)l, aby wstawi(cid:202) go wprost do kontrolera, chocia(cid:285) nie jest to w(cid:239)a(cid:258)ciwe miejsce. Zadaniem kontrolera jest zapewnienie u(cid:285)ytkownikowi zapisania si(cid:218) na spotkanie, i tak powinno pozosta(cid:202). Ale mo(cid:285)emy te(cid:285) wywo(cid:239)a(cid:202) w kontrolerze zdarzenie, które ostrze(cid:285)e wszystkich obserwatorów, (cid:285)e u(cid:285)ytkownik zapisa(cid:239) si(cid:218) na spotkanie. Decyzj(cid:218), co zrobi(cid:202) z t(cid:200) informacj(cid:200), pozostawimy ju(cid:285) obserwatorom. Aby to zdarzenie by(cid:239)o przydatne, musi zawiera(cid:202) dane o u(cid:285)ytkowniku i spotkaniu. Dlatego utwo- rzymy prost(cid:200) klas(cid:218) do przechowywania tych informacji: // Bundle/Event/MeetupEvent.php namespace Khepin\BookBundle\Event; use Symfony\Component\EventDispatcher\Event; use Khepin\BookBundle\Entity\User; use Khepin\BookBundle\Entity\Meetup; 27 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka class MeetupEvent extends Event { protected $user; protected $event; public function __construct(User $user, Meetup $meetup) { $this- user = $user; $this- meetup= $meetup; } public function getUser() { return $this- user; } public function getMeetup() { return $this- meetup; } } Jest to bardzo prosta klasa, której jedynym zadaniem jest przechowywanie danych o zdarzeniu dotycz(cid:200)cym spotkania i u(cid:285)ytkownika. Teraz spowodujemy wyzwolenie tego zdarzenia, gdy u(cid:285)yt- kownik zapisze si(cid:218) na jakie(cid:258) spotkanie. Wpisz poni(cid:285)szy kod w kontrolerze, za kodem sprawdzaj(cid:200)- cym formularz: if ($form- isValid()) { $meetup- addAttendee($user); // To jest nowy wiersz. $this- get( event_dispatcher )- dispatch( meetup.join , new MeetupEvent($user, $meetup) ); $em- flush(); } Wystarczy(cid:239)o znale(cid:283)(cid:202) us(cid:239)ug(cid:218) event_dispatcher i rozes(cid:239)a(cid:202) zdarzenie meetup.join z porcj(cid:200) danych. Rozsy(cid:239)anie zdarzenia to po prostu wys(cid:239)anie wiadomo(cid:258)ci pod pewn(cid:200) nazw(cid:200), w tym przypadku meetup.join, z potencjalnymi danymi. Zanim kod przejdzie do wykonywania nast(cid:218)pnego wiersza, wszystkie klasy i obiekty nas(cid:239)uchuj(cid:200)ce tego zdarzenia równie(cid:285) mog(cid:200) wykona(cid:202) jakie(cid:258) instrukcje. Nazwy zdarze(cid:241) dobrze jest przyporz(cid:200)dkowywa(cid:202) do przestrzeni nazw, aby unikn(cid:200)(cid:202) ewentualnych kolizji. Zazwyczaj do oddzielania przestrzeni nazw zdarze(cid:241) u(cid:285)ywa si(cid:218) kropki i dlatego mo(cid:285)na spotka(cid:202) zdarzenia w stylu acme.user.authentication.success, acme.user.authentication.fail itd. Innym dobrym zwyczajem jest katalogowanie i dokumentowanie swoich zdarze(cid:241). Z do(cid:258)wiad- czenia wiem, (cid:285)e je(cid:258)li dodaje si(cid:218) wiele zdarze(cid:241), „bo tak (cid:239)atwo si(cid:218) je wyzwala, gdy(cid:285) to przecie(cid:285) 28 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe tylko nazwy”, to po pewnym czasie trudno je wszystkie zapami(cid:218)ta(cid:202) i (cid:239)atwo si(cid:218) pogubi(cid:202), do czego s(cid:239)u(cid:285)(cid:200). Katalogowanie zdarze(cid:241) nabiera szczególnego znaczenia, gdy kto(cid:258) planuje udost(cid:218)pnia(cid:202) swój kod innym programistom. Wówczas nale(cid:285)y utworzy(cid:202) statyczn(cid:200) klas(cid:218) zdarze(cid:241): namespace Khepin\BookBundle\Event; final class MeetupEvents { /** * Zdarzenie meetup.join jest wyzwalane, gdy u(cid:298)ytkownik * rejestruje si(cid:266) na spotkaniu. * * Procedury nas(cid:225)uchuj(cid:261)ce otrzymuj(cid:261) egzemplarz obiektu: * Khepin\BookBundle\Event\MeetupEvent */ const MEETUP_JOIN = meetup.join ; } Jak napisa(cid:239)em, klasa ta s(cid:239)u(cid:285)y jedynie do celów dokumentacyjnych. Kod w kontrolerze mo(cid:285)na zmieni(cid:202) nast(cid:218)puj(cid:200)co: $container- get( event_dispatcher )- dispatch( MeetupEvents::MEETUP_JOIN, new MeetupEvent($user, $meetup) ); Wiemy ju(cid:285), jak wyzwoli(cid:202) zdarzenie, ale jak na razie, nie mamy z tej wiedzy wi(cid:218)kszego po(cid:285)yt- ku! Dodamy wi(cid:218)c troch(cid:218) wi(cid:218)cej kodu. Najpierw utworzymy klas(cid:218) nas(cid:239)uchuj(cid:200)c(cid:200), która b(cid:218)dzie odpowiedzialna za generowanie dla u(cid:285)ytkownika nowej listy preferowanych spotka(cid:241): namespace Khepin\BookBundle\Event\Listener; use Khepin\BookBundle\Event\MeetupEvent; class JoinMeetupListener { public function generatePreferences(MeetupEvent $event) { $user = $event- getUser(); $meetup = $event- getMeetup(); // Kod generuj(cid:261)cy nowe preferencje u(cid:298)ytkownika. } } Jest to zwyk(cid:239)a klasa PHP. Nie musi ona niczego specjalnego rozszerza(cid:202), a wi(cid:218)c nie musi te(cid:285) mie(cid:202) jakiej(cid:258) konkretnej nazwy. Najwa(cid:285)niejsze, (cid:285)eby zawiera(cid:239)a jedn(cid:200) metod(cid:218) przyjmuj(cid:200)c(cid:200) argument MeetupEvent. Gdyby(cid:258)my teraz wykonali kod, nic by si(cid:218) nie sta(cid:239)o, poniewa(cid:285) jeszcze nie powie- dzieli(cid:258)my, (cid:285)e ta klasa ma nas(cid:239)uchiwa(cid:202) jakichkolwiek zdarze(cid:241). W tym celu musimy zamieni(cid:202) j(cid:200) w us(cid:239)ug(cid:218). Oznacza to, (cid:285)e naszej procedurze nas(cid:239)uchowej b(cid:218)dzie mo(cid:285)na przekaza(cid:202) egzemplarz us(cid:239)ugi geolokacyjnej, któr(cid:200) zdefiniowali(cid:258)my w pierwszej cz(cid:218)(cid:258)ci rozdzia(cid:239)u, lub dowolnej innej us(cid:239)ugi dost(cid:218)pnej w Symfony. Ponadto w definicji naszej procedury jako us(cid:239)ugi zaobserwujemy te(cid:285) bardziej zaawansowane techniki u(cid:285)ycia us(cid:239)ug: 29 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka join_meetup_listener: class: Khepin\BookBundle\Event\Listener\JoinMeetupListener tags: - { name: kernel.event_listener, event: meetup.join, method: generatePreferences } Sekcja tags oznacza, (cid:285)e przy pierwszym utworzeniu us(cid:239)ugi event_dispatcher zostan(cid:200) wyszu- kane i zapami(cid:218)tane tak(cid:285)e inne us(cid:239)ugi, którym przypisano okre(cid:258)lony znacznik (w tym przypad- ku kernel.event_listener). Jest to wykorzystywane równie(cid:285) przez inne sk(cid:239)adniki Symfony, np. system formularzy (omówiony w rozdziale 3.). Poprawianie wydajno(cid:258)ci Osi(cid:200)gn(cid:218)li(cid:258)my pewien cel przy u(cid:285)yciu zdarze(cid:241) i procedur nas(cid:239)uchuj(cid:200)cych. Ca(cid:239)a logika doty- cz(cid:200)ca obliczania preferencji u(cid:285)ytkownika znajduje si(cid:218) w osobnej klasie nas(cid:239)uchowej. Nie przed- stawi(cid:239)em szczegó(cid:239)owo implementacji tej logiki, ale wiadomo ju(cid:285), (cid:285)e najlepiej wynie(cid:258)(cid:202) j(cid:200) poza kontroler i przekszta(cid:239)ci(cid:202) w niezale(cid:285)n(cid:200) us(cid:239)ug(cid:218) z mo(cid:285)liwo(cid:258)ci(cid:200) wywo(cid:239)ywania w procedurze nas(cid:239)u- chuj(cid:200)cej. Im wi(cid:218)cej b(cid:218)dziesz u(cid:285)ywa(cid:202) Symfony, tym bardziej oczywiste b(cid:218)dzie Ci si(cid:218) to wydawa(cid:202). Ca(cid:239)y kod, który mo(cid:285)na przenie(cid:258)(cid:202) do us(cid:239)ugi, nale(cid:285)y przenie(cid:258)(cid:202) do us(cid:239)ugi. Niektórzy programi(cid:258)ci rdzenia Symfony twierdz(cid:200), (cid:285)e nawet kontrolery powinny by(cid:202) us(cid:239)ugami. Je(cid:258)li zastosujesz si(cid:218) do tych wskazówek, Twój kod b(cid:218)dzie (cid:239)atwiejszy do testowania. Kod dzia(cid:239)aj(cid:200)cy po odpowiedzi Gdy witryna stanie si(cid:218) bardziej skomplikowana i b(cid:218)dzie mia(cid:239)a du(cid:285)o u(cid:285)ytkowników, obliczenia preferowanych typów zdarze(cid:241) u(cid:285)ytkowników mog(cid:200) si(cid:218) d(cid:239)u(cid:285)y(cid:202). Poza tym u(cid:285)ytkownik mo(cid:285)e mie(cid:202) przyjació(cid:239) na naszej stronie, w zwi(cid:200)zku z czym chcieliby(cid:258)my, aby jego wybory mia(cid:239)y wp(cid:239)yw tak(cid:285)e na preferencje jego znajomych. W nowoczesnych aplikacjach sieciowych cz(cid:218)sto nie trzeba czeka(cid:202) na zako(cid:241)czenie czasoch(cid:239)onnych operacji, zanim zostanie zwrócona odpowied(cid:283) do u(cid:285)ytkownika. Oto niektóre z takich przypadków: (cid:81) Po wys(cid:239)aniu filmu na serwer u(cid:285)ytkownik nie powinien czeka(cid:202) na zako(cid:241)czenie konwersji tego filmu na inny format, a(cid:285) pojawi si(cid:218) strona z informacj(cid:200), (cid:285)e wysy(cid:239)anie zako(cid:241)czy(cid:239)o si(cid:218) pomy(cid:258)lnie. (cid:81) Kilka sekund mo(cid:285)na zyska(cid:202), je(cid:258)li nie b(cid:218)dzie si(cid:218) zmienia(cid:202) rozmiaru obrazu profilowego u(cid:285)ytkownika przed wy(cid:258)wietleniem informacji, (cid:285)e aktualizacja si(cid:218) powiod(cid:239)a. (cid:81) W naszym przypadku u(cid:285)ytkownik nie powinien czeka(cid:202) na potwierdzenie, a(cid:285) roze(cid:258)lemy wszystkim jego znajomym informacj(cid:218), (cid:285)e zapisa(cid:239) si(cid:218) na jakie(cid:258) spotkanie. Problemy te mo(cid:285)na rozwi(cid:200)za(cid:202) na wiele sposobów, aby odci(cid:200)(cid:285)y(cid:202) proces generowania odpowiedzi. Mo(cid:285)na codziennie oblicza(cid:202) preferencje u(cid:285)ytkownika za pomoc(cid:200) procesów wsadowych, ale to spowoduje opó(cid:283)nienia w zwracaniu odpowiedzi, poniewa(cid:285) aktualizacje b(cid:218)d(cid:200) wykonywane tylko raz dziennie, oraz mo(cid:285)e to prowadzi(cid:202) do marnowania zasobów. Mo(cid:285)na te(cid:285) u(cid:285)y(cid:202) kolejki wiadomo(cid:258)ci i robotników w taki sposób, (cid:285)e kolejka powiadamia(cid:239)aby robotników o konieczno(cid:258)ci 30 Kup książkęPoleć książkę Rozdzia(cid:225) 1. • Us(cid:225)ugi i procedury nas(cid:225)uchowe zrobienia czego(cid:258). By(cid:239)oby to co(cid:258) podobnego do rozwi(cid:200)zania ze zdarzeniami, ale kod wykonuj(cid:200)- cy obliczenia dzia(cid:239)a(cid:239)by w innym procesie, a mo(cid:285)e nawet na innej maszynie. Nie trzeba by by(cid:239)o te(cid:285) czeka(cid:202) na jego zako(cid:241)czenie, aby móc kontynuowa(cid:202). W Symfony problem ten mo(cid:285)na (cid:239)atwo rozwi(cid:200)za(cid:202), pozostaj(cid:200)c ca(cid:239)y czas w systemie. Nas(cid:239)uchu- j(cid:200)c zdarzenia kernel.terminate, mo(cid:285)emy uruchomi(cid:202) metod(cid:218) naszej procedury nas(cid:239)uchuj(cid:200)cej po tym, jak odpowied(cid:283) zostanie wys(cid:239)ana do klienta. Zmienimy nasz kod, aby skorzysta(cid:202) z tej mo(cid:285)liwo(cid:258)ci. Nasza nowa procedura nas(cid:239)uchuj(cid:200)ca b(cid:218)dzie teraz zachowywa(cid:202) si(cid:218) tak, jak napisano w poni(cid:285)szej tabeli: Zdarzenie Procedura nas(cid:239)uchuj(cid:200)ca meetup.join kernel.terminate Zapami(cid:218)tuje u(cid:285)ytkownika i spotkanie na pó(cid:283)niej. Brak jakichkolwiek oblicze(cid:241). Generuje preferencje u(cid:285)ytkownika. Wykonuje obliczenia. Nasz kod powinien teraz wygl(cid:200)da(cid:202) tak: class JoinMeetupListener { protected $event; public function onUserJoinsMeetup(MeetupEvent $event) { $this- event = $event; } public function generatePreferences() { if ($this- event) { // Generuje nowe preferencje u(cid:298)ytkownika. } } } Nast(cid:218)pnie musimy te(cid:285) zmieni(cid:202) konfiguracj(cid:218), aby wywo(cid:239)ywa(cid:239)a generatePreferences w przy- padku wyst(cid:200)pienia zdarzenia kernel.terminate: join_meetup_listener: class: Khepin\BookBundle\Event\Listener\JoinMeetupListener tags: - { name: kernel.event_listener, event: meetup.join, method: onUserJoinsMeetup } - { name: kernel.event_listener, event: kernel.terminate, method: generatePreferences } Wystarczy(cid:239)o doda(cid:202) znacznik do istniej(cid:200)cej procedury nas(cid:239)uchowej. Je(cid:258)li rozwa(cid:285)a(cid:239)e(cid:258) utworze- nie nowej us(cid:239)ugi tej samej klasy, tylko nas(cid:239)uchuj(cid:200)cej innego zdarzenia, teraz b(cid:218)dziesz mie(cid:202) dwa ró(cid:285)ne egzemplarze us(cid:239)ugi. W zwi(cid:200)zku z tym us(cid:239)uga, która zapami(cid:218)ta(cid:239)a zdarzenie, nigdy 31 Kup książkęPoleć książkę Symfony2. Rozbudowa frameworka nie zostanie wywo(cid:239)ana w celu wygenerowania preferencji, a us(cid:239)uga wywo(cid:239)ana w celu wygene- rowania preferencji nigdy nie otrzyma zdarzenia do pracy. Dzi(cid:218)ki tej nowej konfiguracji kod wykonuj(cid:200)cy intensywne obliczenia nie przeszkadza ju(cid:285) w wysy(cid:239)aniu odpowiedzi do u(cid:285)ytkow- nika, który mo(cid:285)e cieszy(cid:202) si(cid:218) komfortowym przegl(cid:200)daniem stron. Podsumowanie W niniejszym rozdziale zosta(cid:239)y wprowadzone dwa podstawowe poj(cid:218)cia systemu Symfony, zw(cid:239)aszcza je(cid:258)li chodzi o tworzenie rozszerze(cid:241). Na przyk(cid:239)adzie geokodowania dowiedzia(cid:239)e(cid:258) si(cid:218), jak (cid:239)atwo dodaje si(cid:218) us(cid:239)ugi podobne do standardowych us(cid:239)ug systemu. Ponadto pokaza(cid:239)em, jak za pomoc(cid:200) zdarze(cid:241) odpowiednio rozdysponowa(cid:202) logik(cid:218) programu, aby nie za(cid:258)mieci(cid:202) kontro- lerów niechcianym kodem. Na zako(cid:241)czenie przy u(cid:285)yciu zdarze(cid:241) przyspieszyli(cid:258)my dzia(cid:239)anie witryny i uczynili(cid:258)my przegl(cid:200)danie stron bardziej komfortowym. Mo(cid:285)esz wierzy(cid:202) lub nie, ale je(cid:258)li dobrze zrozumiesz dzia(cid:239)anie zdarze(cid:241) i us(cid:239)ug, to b(cid:218)dziesz wiedzie(cid:202) prawie wszystko na temat rozszerzania Symfony. W dalszej cz(cid:218)(cid:258)ci ksi(cid:200)(cid:285)ki b(cid:218)dziemy wielokrotnie wraca(cid:202) do tych dwóch poj(cid:218)(cid:202), a wi(cid:218)c jest bardzo wa(cid:285)ne, aby je dobrze zrozumie(cid:202). W nast(cid:218)pnym rozdziale dodamy nowe polecenia do narz(cid:218)dzia konsolowego Symfony oraz do- stosujemy do swoich potrzeb silnik szablonów. W tym równie(cid:285) bardzo pomocne b(cid:218)d(cid:200) us(cid:239)ugi. 32 Kup książkęPoleć książkę Skorowidz DIC, dependency injection container, 17 Doctrine, 25, 89 dodawanie adnotacji, 82 mapy do widoku, 51 pól, 60 dokumentacja, 116 dokumentowanie zdarze(cid:241), 28 dostawca u(cid:285)ytkowników, 68 dost(cid:218)p do geokodera, 15 dystrybucja, 118 dziedziczenie us(cid:239)ugi, 72 F fabryka zabezpiecze(cid:241), 68, 108 filtr, 103 ró(cid:285)nicy czasowej, 44 formularz, 27, 47 jako us(cid:239)uga, 56 funkcja buildView(), 56 configure(), 34 execute(), 34 parse(), 96 G H geokoder, 21 geolokalizacja, 14 has(cid:239)o, 67 A abstrakcyjna definicja us(cid:239)ugi, 72 ACL, access control list, 74 adnotacja, 74, 80 @Annotation, 80 @ORM\Version, 99 aktualizowanie preferencji u(cid:285)ytkownika, 27 wersji, 100 API, 85 aplikacja GitHub, 110 atak typu CSRF, 27, 87 atrapy klas, 19 atrybuty znacznika, 24 autoryzacja, 63, 74 awatar, 33 baza danych MongoDB, 89, 90 MySQL, 89 PostgreSQL, 89 bezpiecze(cid:241)stwo, 63 biblioteka Imagine, 34 b(cid:239)(cid:200)d, 44 B D dane u(cid:285)ytkowników, 56 zdarzenia, 62 definiowanie adnotacji, 80 us(cid:239)ugi, 24 Kup książkęPoleć książkę Skorowidz I inicjacja geokodowania, 15 integracja z mapami Google, 48 interfejs API, 85 do us(cid:239)ug, 38 Geocoder, 21 PHP, 104 UserOwnedEntity, 104 VoterInterface, 75 jednostka robocza, 101 J K klasa adaptacyjna, 16 Address, 58 adnotacyjna, 80 AuthenticationListener, 68, 110 AuthenticationProvider, 71 BaseController, 17 Coordinate, 48, 55 dostawcza, 16 Form, 57 geokodowania, 16 KhepinGitAuthBundle, 108 OwnerFilter, 105 PHPUnit_Framework_TestCase, 19, 37 Token, 67 Type, 51, 90 UserLocator, 19 UserProvider, 68, 73 Voter, 75 WebTestCase, 19, 37 kod dzia(cid:239)aj(cid:200)cy po odpowiedzi, 30 kompilacja DIC, 23 konfiguracja formularza, 49 ORM, 104 konsola, 36 kontener wstrzykiwania zale(cid:285)no(cid:258)ci, 17 kontrola wersji, 97 122 L licencja MIT, 119 licencjonowanie, 118 lista kontroli dost(cid:218)pu, 74 pakietów, 116 lokalizacja predefiniowana, 55 M mapowanie, 92 obiektowo-relacyjne, 89 mapy Google, 47 metoda attemptAuthentication, 66 closureToDatabase, 90 closureToPHP, 90, 91 convertToDatabaseValue, 90 convertToPHPValue, 90 createView, 51 get*Annotations, 82 getClass, 82 getForm, 51 getKey, 70 getUserCoordinate, 57 prePersist, 99 preUpdate, 99 reverseTransform, 54 supportClass, 75 supportsAttribute, 75 Trait, 98 transform, 54 vote, 75 modyfikowanie formularza, 60, 61 N narz(cid:218)dzia narz(cid:218)dzie do geokodowania, 14 do testowania, 117 do mapowania obiektowo-relacyjnego, 89 Mockery, 117 nazwa przestrzeni nazw, 108 nazwy zdarze(cid:241), 28 Kup książkęPoleć książkę Skorowidz O odczytywanie adnotacji, 82 ODM, object-document mapper, 89 ODM Mongo, 92 opcja compound, 50 ORM, 89 P FOSUserBundle, 72 ODM Mongo, 92 SensioFrameworkExtraBundle, 82 pakiet parser, 95 plik bootstrap.php, 117 config.yml, 19, 94, 109 Configuration.php, 113 Extension.php, 113 phpunit.xml, 117 README, 116 routing.yml, 66 pliki zabezpiecze(cid:241), 72 polecenia, 33 jako interfejs do us(cid:239)ug, 38 polecenie picture:resize, 37 procedura nas(cid:239)uchuj(cid:200)ca uwierzytelniania, 66 procedury nas(cid:239)uchuj(cid:200)ce, 25, 62 przekszta(cid:239)canie danych, 54 przestrze(cid:241) nazw, 108 R relacja wiele do wielu, 26 reprezentacje danych, 54 rozmiar obrazów, 34 S sekcja tags, 30 serwer relacyjnych baz danych, 10 serwis GitHub, 64, 66 sk(cid:239)adnia funkcji DQL, 96 skrypt, 41 struktura pakietu, 108 system sqlite, 101 Symfony, 10 szablonów Twig, 40 szablony Twig, 40 T technologia OAuth, 64 testowanie bazy danych, 101 mapowania, 92 pakietu, 116 polecenia, 37 rozszerze(cid:241) Twig, 43 us(cid:239)ug, 19 token, 68, 72 token DISTANCE, 95 transformatory danych, 54 Twig, 40, 41 rozszerzenia, 40 testowanie rozszerze(cid:241), 43 tworzenie filtra Doctrine, 103 formularzy, 47 map, 51 pakietu, 107, 113 w(cid:239)asnych typów danych, 89 U udost(cid:218)pnianie konfiguracji, 110 pakietu, 116 w(cid:239)asnych rozszerze(cid:241), 107 uprawnienia szczegó(cid:239)owe, 75 us(cid:239)uga, service, 13 event_dispatcher, 28 fos_user.user_manager, 38 geolokalizacji, 14, 29 ivory_google_map.map, 51 shrinker, 39 user_locator, 24, 56, 57 us(cid:239)ugi domy(cid:258)lne, 13 ustawianie wersji, 99 usuwanie pól, 60 uwierzytelnianie, 63 u(cid:285)ywanie mapy, 51 wersji, 100 OAuth poprzez GitHub, 64 123 Kup książkęPoleć książkę zakres container, 18 prototype, 18, 51 request, 18 zapora ogniowa, 65, 68 zarz(cid:200)dzanie skryptami, 41 zdarzenia w(cid:239)asne, 27 zdarzenie kernel.controller, 25 kernel.exception, 25 kernel.request, 25, 105 kernel.response, 25 kernel.terminate, 25, 31 kernel.view, 25 loadClassMetadata, 98 meetup.join, 31 onClear, 98 post*, 98 POST_SET_DATA, 57 POST_SUBMIT, 57 postFlush, 98 postLoad, 98 PRE_SET_DATA, 56 PRE_SUBMIT, 57 prePersist, 98 preRemove, 98 preUpdate, 98 SUBMIT, 57 zmiany w tokenie, 71 zmienianie rozmiaru obrazów, 34 znacznik script , 42 znakowanie us(cid:225)ug, 21 Skorowidz Votery, 75 V W Walker AST, 103 warstwa abstrakcji baz danych, 97 wersjonowanie, 100 weryfikowanie poprawno(cid:258)ci pakietu, 113 wid(cid:285)et wy(cid:258)wietlaj(cid:200)cy map(cid:218), 47, 50 wiersz polece(cid:241) Composera, 33 w(cid:239)asne funkcje DQL, 93 funkcje SQL, 93 rozszerzenia, 107 typy danych, 89 zdarzenia, 27 wspó(cid:239)rz(cid:218)dne geograficzne, 47 wstrzykiwanie zale(cid:285)no(cid:258)ci, 17 wydajno(cid:258)(cid:202), 30 wyj(cid:200)tek, 51 wy(cid:258)wietlanie listy us(cid:239)ug, 13 formularza, 59 pyta(cid:241), 36 wyzwalanie zdarze(cid:241), 26 wzorzec Obserwator, 25 wzór na odleg(cid:239)o(cid:258)(cid:202) punktów, 93 Z zabezpieczanie API, 85 kontrolerów, 83 124 Kup książkęPoleć książkę
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Symfony2. Rozbudowa frameworka
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: