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)