• Название:

    Symfony

  • Размер: 2.47 Мб
  • Формат: PDF
  • или

    Symfony Documentation
    Âûïóñê 2.0

    Fabien Potencier

    15 January 2012

    Îãëàâëåíèå
    I

    Êðàòêèé Îáçîð

    1

    1 Êðàòêèé òóð

    5

    II Ðóêîâîäñòâà

    7

    2 Êíèãà
    2.1
    2.2
    2.3
    2.4
    2.5
    2.6
    2.7
    2.8
    2.9
    2.10
    2.11
    2.12
    2.13
    2.14
    2.15
    2.16
    2.17

    Symfony2 è îñíîâû HTTP . . . . . . .
    Symfony2 ïðîòèâ ÷èñòîãî PHP . . . .
    Óñòàíîâêà è íàñòðîéêà Symfony2 . . .
    Ñîçäàíèå ñòðàíèö â Symfony2 . . . . .
    Êîíòðîëëåð . . . . . . . . . . . . . . .
    Ìàðøðóòèçàöèÿ . . . . . . . . . . . . .
    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ .
    Áàçû äàííûõ è Doctrine (Ìîäåëü) . .
    Òåñòèðîâàíèå . . . . . . . . . . . . . .
    Âàëèäàöèÿ . . . . . . . . . . . . . . . .
    Ôîðìû . . . . . . . . . . . . . . . . . .
    Áåçîïàñíîñòü . . . . . . . . . . . . . . .
    HTTP Êýøèðîâàíèå . . . . . . . . . .
    Ïåðåâîäû . . . . . . . . . . . . . . . . .
    Êîíòåéíåð ñëóæá . . . . . . . . . . . .
    Ñîñòàâíûå ÷àñòè . . . . . . . . . . . .
    Ñòàáèëüíûé API Symfony2 . . . . . . .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    11

    11
    22
    37
    42
    61
    75
    98
    121
    148
    162
    179
    210
    247
    267
    283
    304
    326

    i

    III

    Ðåöåïòû

    3 Cookbook
    3.1
    3.2
    3.3
    3.4
    3.5
    3.6
    3.7
    3.8
    3.9
    3.10
    3.11
    3.12
    3.13
    3.14
    3.15

    IV

    Êàê ñîçäàòü è ðàçìåñòèòü Ïðîåêò íà Symfony2 â git-ðåïîçèòîðèè . . .
    Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê . . . . . . . . . . . . . . . .
    Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ . . . . . . . . . . . .
    Êàê çàñòàâèòü ìàðøðóòèçàòîð âñåãäà èñïîëüçîâàòü HTTPS èëè HTTP
    Êàê îòïðàâëÿòü ýëåêòðîííóþ ïî÷òó . . . . . . . . . . . . . . . . . . . .
    Êàê èñïîëüçîâàòü Gmail äëÿ îòïðàâêè ýëåêòðîííûõ ïèñåì . . . . . . .
    Êàê ñìîäåëèðîâàòü HTTP àóòåíòèôèêàöèþ â Ôóíêöèîíàëüíîì òåñòå .
    Êàê òåñòèðîâàòü âçàèìîäåéñòâèå ñ íåñêîëüêèìè êëèåíòàìè . . . . . . .
    Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå . . . . . .
    Êàê èñïîëüçîâàòü Varnish äëÿ óñêîðåíèÿ ðàáîòû ñàéòà . . . . . . . . .
    Âíåäðåíèå ïåðåìåííûõ âî âñå øàáëîíû (ò.í. Ãëîáàëüíûå ïåðåìåííûå)
    Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig . . . . . . . . . . . . . . .
    Êàê èñïîëüçîâàòü Monolog äëÿ æóðíàëèðîâàíèÿ . . . . . . . . . . . . .
    Êàê àâòîìàòè÷åñêè çàãðóæàòü êëàññû . . . . . . . . . . . . . . . . . . .
    Êàê èñêàòü ôàéëû . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    Ñïðàâî÷íûå äîêóìåíòû

    329
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    331

    331
    334
    336
    337
    338
    341
    342
    342
    343
    345
    346
    347
    352
    356
    359

    367

    4 Reference Documents

    371

    V

    387

    4.1
    4.2
    4.3

    Ñïðàâî÷íèê òèïîâ ïîëåé äëÿ ôîðì . . . . . . . . . . . . . . . . . . . . . . 371
    Ñïðàâî÷íèê ôóíêöèé Twig äëÿ ðàáîòû ñ ôîðìàìè . . . . . . . . . . . . . 375
    Òàãè Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . 376

    Ïàêåòû

    5 Symfony SE Bundles

    391

    VI

    393

    Ó÷àñòèå â ïðîåêòå

    6 Ñîäåéñòâèå

    397

    Àëôàâèòíûé óêàçàòåëü

    405

    6.1
    6.2
    6.3

    ii

    Ñîäåéñòâèå â êîäå . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
    Ñîäåéñòâèå â äîêóìåíòàöèè . . . . . . . . . . . . . . . . . . . . . . . . . . 400
    Ñîäåéñòâèå â êîäå . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403

    ×àñòü I
    Êðàòêèé Îáçîð

    1

    Symfony Documentation, Âûïóñê 2.0

    Áûñòðûé ñòàðò ñ Symfony2

    Êðàòêèé Îáçîð :

    3

    Symfony Documentation, Âûïóñê 2.0

    4

    Ãëàâà

    1

    Êðàòêèé òóð
    Áûñòðûé ñòàðò ñ êðàòêèì òóðîì ïî Symfony2:
    ˆ quick_tour/the_big_picture >
    ˆ quick_tour/the_view >
    ˆ quick_tour/the_controller >
    ˆ quick_tour/the_architecture

    5

    Symfony Documentation, Âûïóñê 2.0

    6

    Ãëàâà 1.

    Êðàòêèé òóð

    ×àñòü II
    Ðóêîâîäñòâà

    7

    Symfony Documentation, Âûïóñê 2.0

    Ïîãðóçèòåñü â ìèð Symfony2 ñ ïîìîùüþ àêòóàëüíûõ ðóêîâîäñòâ:

    9

    Symfony Documentation, Âûïóñê 2.0

    10

    Ãëàâà

    2

    Êíèãà
    2.1 Symfony2 è îñíîâû HTTP
    Ïîçäðàâëÿåì! Íà÷àâ èçó÷åíèå Symfony2, âû âñòàëè íà ïðàâèëüíûé ïóòü, ÷òîáû ñòàòü
    áîëåå ïðîäóêòèâíûì, âñåñòîðîííå ðàçâèòûì è ïîïóëÿðíûì âåá-ðàçðàáîò÷èêîì (õîòÿ
    ïîñëåäíåå - íà âàøå óñìîòðåíèå). Symfony2 ñîçäàí, ÷òîáû ïðåäîñòàâëÿòü áàçîâûå, íèçêîóðîâíåâûå èíñòðóìåíòû, ïîçâîëÿþùèå âàì ðàçðàáàòûâàòü áûñòðåå, ñîçäàâàòü áîëåå
    íàä¼æíûå ïðèëîæåíèÿ, íî ïðè ýòîì áûòü â ñòðîíå îò âàøåãî ñîáñòâåííîãî ïóòè. Symfony
    ïîñòðîåí íà ëó÷øèõ èäåÿõ, çàèìñòâîâàííûõ èç ðàçëè÷íûõ òåõíîëîãèé: èíñòðóìåíòû è
    êîíöåïöèè, êîòîðûå âû ãîòîâèòåñü èçó÷èòü - ïðåäñòàâëåíû óñèëèÿìè òûñÿ÷ è òûñÿ÷
    ëþäåé íà ïðîòÿæåíèè ìíîãèõ ëåò. Äðóãèìè ñëîâàìè, âû íå òîëüêî èçó÷àåòå Symfony,
    âû èçó÷àåòå îñíîâû web, ëó÷øèå ïðàêòèêè ðàçðàáîòêè, à òàêæå ñïîñîáû èñïîëüçîâàíèÿ ìíîãèõ çàìå÷àòåëüíûõ PHP-áèáëèîòåê â ñîñòàâå Symfony2 èëè íå çàâèñèìî îò íåãî.
    Èòàê, ïðèãîòîâüòåñü.
    Ñëåäóÿ ôèëîñîôèè Symfony2, ýòà ãëàâà íà÷èíàåòñÿ ñ îáúÿñíåíèÿ îñíîâíîé êîíöåïöèè,
    òèïè÷íîé äëÿ web-ðàçðàáîòêè: HTTP. Íå çàâèñèìî îò âàøåãî îïûòà èëè ëþáèìîãî
    ÿçûêà ïðîãðàììèðîâàíèÿ, ýòà ãëàâà îáÿçàòåëüíà ê ïðî÷òåíèþ âñåì.
    2.1.1 HTTP ýòî Ïðîñòî

    HTTP (Hypertext Transfer Protocol èëè ïðîñòî Ïðîòîêîë Ïåðåäà÷è Ãèïåðòåêñòà) - ýòî
    òåêñòîâûé ÿçûê, ïîçâîëÿþùèé äâóì êîìïüþòåðàì îáìåíèâàòüñÿ ñîîáùåíèÿìè äðóã ñ
    äðóãîì. Âîò è âñ¼! Íàïðèìåð, êîãäà ìû õîòèì ïîñìîòðåòü íîâåíüêèé êîìèêñ xkcd, èìååò
    ìåñòî (ïðèìåðíî) òàêîé äèàëîã:

    11

    Symfony Documentation, Âûïóñê 2.0

    È ïîêà èñïîëüçóåòñÿ ðåàëüíûé ÿçûê, õîòÿ îí è íåñêîëüêî áîëåå ôîðìàëüíûé, îí îñòà¼òñÿ ïðåäåëüíî ïðîñòûì. HTTP - ýòî òåðìèí, èñïîëüçóåìûé äëÿ îïèñàíèÿ ýòîãî ïðîñòîãî
    òåêñòîâîãî ÿçûêà. È íå âàæíî, êàê èìåííî âû ðàçðàáàòûâàåòå â web, öåëüþ âàøåãî ñåðâåðà âñåãäà ÿâëÿåòñÿ ïîíÿòü ïðîñòîé òåêñòîâûé çàïðîñ è âåðíóòü ïðîñòîé òåêñòîâûé
    îòâåò.
    Symfony2 âîçâûøàåòñÿ íàä ýòîé ðåàëüíîñòüþ. ×òî áû âû íè äåëàëè, HTTP - ýòî òî, ÷òî
    âû èñïîëüçóåòå åæåäíåâíî. Ñ ïîìîùüþ Symfony2 âû óçíàåòå, êàê óïðàâëÿòü èì.
    Øàã 1: Êëèåíò îòïðàâëÿåò çàïðîñ

    Ëþáîé äèàëîã â ñåòè íà÷èíàåòñÿ ñ çàïðîñà. Çàïðîñ - ýòî òåêñòîâîå ñîîáùåíèå, ñîçäàâàåìîå êëèåíòîì (íàïðèìåð áðàóçåðîì èëè iPhone ïðèëîæåíèåì è ò.ä.) â îñîáîì ôîðìàòå,
    òàêæå èçâåñòíîì êàê HTTP. Êëèåíò îòïðàâëÿåò ýòîò çàïðîñ ñåðâåðó, è îæèäàåò îòâåò.
    Âçãëÿíèòå íà ïåðâóþ ÷àñòü âçàèìîäåéñòâèÿ (çàïðîñ) ìåæäó áðàóçåðîì è âåá-ñåðâåðîì
    xkcd:

    Íà ÿçûêå HTTP ýòîò çàïðîñ áóäåò âûãëÿäåòü ïðèìåðíî òàê:
    12

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    GET / HTTP/1.1
    Host: xkcd.com
    Accept: text/html
    User-Agent: Mozilla/5.0 (Macintosh)

    Ýòî ïðîñòîå ñîîáùåíèå ñîäåðæèò âñþ íåîáõîäèìóþ èíôîðìàöèþ î òîì, êàêîé èìåííî
    ðåñóðñ çàïðàøèâàåò êëèåíò. Ïåðâàÿ ñòðîêà HTTP çàïðîñà íàèáîëåå âàæíà - îíà ñîäåðæèò 2 âåùè: çàïðîøåííûé URI è HTTP-ìåòîä.
    URI (íàïðèìåð /, /contact, è ò.ä.) - ýòî óíèêàëüíûé àäðåñ èëè ìåñòî, êîòîðîå îïðåäåëÿåò çàïðîøåííûé êëèåíòîì ðåñóðñ. HTTP-ìåòîä (íàïðèìåð GET) îïðåäåëÿåò, ÷òî èìåííî
    âû õîòèòå ñäåëàòü ñ çàïðîøåííûì ðåñóðñîì. HTTP ìåòîäû ýòî ãëàãîëû â çàïðîñå è
    îíè îïðåäåëÿþò íåñêîëüêî òèïè÷íûõ ïóòåé, êîòîðûìè âû ìîæåòå âçàèìîäåéñòâîâàòü ñ
    çàïðîøåííûì ðåñóðñîì:
    GET
    POST
    PUT
    DELETE

    Ïîëó÷èòü ðåñóðñ ñ ñåðâåðà
    Ñîçäàòü ðåñóðñ íà ñåðâåðå
    Îáíîâèòü ðåñóðñ íà ñåðâåðå
    Óäàëèòü ðåñóðñ ñ ñåðâåðà

    Çàïîìíèâ ýòè òèïû HTTP-ìåòîäîâ, âû ìîæåòå ïðåäñòàâèòü ñåáå, êàê áóäåò âûãëÿäåòü
    HTTP-çàïðîñ íà óäàëåíèå çàïèñè â áëîãå:
    DELETE /blog/15 HTTP/1.1

    Ïðèìå÷àíèå: Íà ñàìîì äåëå âñåãî ñóùåñòâóåò äåâÿòü HTTP-ìåòîäîâ, îïðåäåë¼ííûõ

    â ñïåöèôèêàöèè ïðîòîêîëà HTTP, íî ìíîãèå èç íèõ î÷åíü ìàëî ðàñïðîñòðàíåíû èëè
    æå îãðàíè÷åííî ïîääåðæèâàþòñÿ. Ê ïðèìåðó, ìíîãèå ñîâðåìåííûå áðàóçåðû íå ïîääåðæèâàþò ìåòîäû PUT è DELETE.
     äîïîëíåíèå ê ïåðâîé ñòðîêå, HTTP-çàïðîñ âñåãäà ñîäåðæèò íåñêîëüêî èíôîðìàöèîííûõ ñòðîê, èìåíóåìûõ çàãîëîâêàìè (headers). Çàãîëîâêè ìîãóò ïðåäîñòàâëÿòü ðàçëè÷íóþ èíôîðìàöèþ, òàêóþ êàê çàïðîøåííûé Host, ôîðìàòû îòâåòà, êîòîðûå ïîääåðæèâàåò êëèåíò (Accept) è ïðèëîæåíèå, èñïîëüçóåìîå êëèåíòîì äëÿ âûïîëíåíèÿ çàïðîñà
    (User-Agent). Ñóùåñòâóåò òàêæå ìíîãî äðóãèõ çàãîëîâêîâ, ïåðå÷åíü êîòîðûõ âû ìîæåòå íàéòè â Âèêèïåäèè íà ñòðàíèöå List of HTTP header elds.
    Øàã 2: Ñåðâåð âîçâðàùàåò îòâåò

    Ñ òîãî ìîìåíòà êàê ñåðâåð ïîëó÷èë çàïðîñ, îí òî÷íî çíàåò, êàêîé ðåñóðñ íóæåí êëèåíòó
    (îñíîâûâàÿñü íà URI) è ÷òî êëèåíò õî÷åò ñ ýòèì ðåñóðñîì ñäåëàòü - íà îñíîâàíèè HTTPìåòîäà. Íàïðèìåð, â ñëó÷àå GET-çàïðîñà, ñåðâåð ïîäãîòîâèò çàïðîøåííûé ðåñóðñ è
    âîçâðàòèò åãî â âèäå HTTP-îòâåòà. Ðàññìîòðèì îòâåò îò web ñåðâåðà xkcd:

    2.1.

    Symfony2 è îñíîâû HTTP

    13

    Symfony Documentation, Âûïóñê 2.0

    Ïåðåâåä¼ííûé â ôîðìàò HTTP, îòâåò, îòïðàâëåííûé îáðàòíî â áðàóçåð, áóäåò âûãëÿäåòü ïðèìåðíî òàê:
    HTTP/1.1 200 OK
    Date: Sat, 02 Apr 2011 21:05:05 GMT
    Server: lighttpd/1.4.19
    Content-Type: text/html




    HTTP-îòâåò ñîäåðæèò çàïðîøåííûé ðåñóðñ (â äàííîì ñëó÷àå ýòî HTML-êîä ñòðàíèöû), à òàêæå äîïîëíèòåëüíûå äàííûå î ñàìîì îòâåòå. Ïåðâàÿ ñòðîêà îñîáåííî âàæíà îíà ñîäåðæèò HTTP ñòàòóñ-êîä (â äàííîì ñëó÷àå 200). Ñòàòóñ-êîä ñîîáùàåò î ðåçóëüòàòå âûïîëíåíèÿ çàïðîñà, íàïðàâëÿåìîì êëèåíòó. Áûë ëè çàïðîñ óñïåøåí? Áûëà ëè â
    õîäå âûïîëíåíèÿ çàïðîñà îøèáêà? Îäíè ñòàòóñ-êîäû îáîçíà÷àþò óñïåøíûå çàïðîñû,
    äðóãèå - îøèáêè, òðåòüè ñîîáùàþò, ÷òî êëèåíò äîëæåí âûïîëíèòü ÷òî-ëèáî (íàïðèìåð
    ïåðåíàïðàâëåíèå íà äðóãóþ ñòðàíèöó). Ïîëíûé ñïèñîê âû ìîæåòå íàéòè ñòðàíèöå List
    of HTTP status codes â Âèêèïåäèè.
    Ïîäîáíî çàïðîñó, HTTP-îòâåò ñîäåðæèò äîïîëíèòåëüíóþ èíôîðìàöèþ, íàçûâàåìóþ HTTP-çàãîëîâêàìè. Íàïðèìåð, âàæíûì çàãîëîâêîì HTTP-îòâåòà ÿâëÿåòñÿ
    Content-Type. Òåëî îäíîãî è òîãî æå ðåñóðñà ìîæåò áûòü âîçâðàùåíî âî ìíîæåñòâå
    ðàçëè÷íûõ ôîðìàòîâ, âêëþ÷àÿ HTML, XML èëè JSON. Çàãîëîâîê Content-Type ñîîáùàåò êëèåíòó, êàêîé èìåííî ôîðìàò èñïîëüçóåòñÿ â äàííîì îòâåòå.
    Ñóùåñòâóåò ìíîãî ðàçëè÷íûõ çàãîëîâêîâ, íåêîòîðûå èç íèõ ïðåäîñòàâëÿþò áîëüøèå
    âîçìîæíîñòè. Íàïðèìåð, íåêîòîðûå çàãîëîâêè ìîãóò áûòü èñïîëüçîâàíû äëÿ ñîçäàíèÿ
    ñèñòåìû êýøèðîâàíèÿ.

    14

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Çàïðîñû, Îòâåòû è Web-ðàçðàáîòêà

    Îáìåí çàïðîñàìè-îòâåòàìè - ýòî ôóíäàìåíòàëüíûé ïðîöåññ, êîòîðûé äâèæåò âñå êîììóíèêàöèè âî âñåìèðíîé ñåòè. È íàñêîëüêî âàæåí ýòîò ïðîöåññ, íàñòîëüêî îí ïðîñò.
    Íàèáîëåå âàæíûì ÿâëÿåòñÿ ñëåäóþùèé ôàêò: âíå çàâèñèìîñòè îò òîãî, êàêîé ÿçûêà
    ïðîãðàììèðîâàíèÿ âû èñïîëüçóåòå, êàêîå ïðèëîæåíèå ñîçäà¼òå (web, ìîáèëüíîå, JSON
    API) è äàæå êàêîé ôèëîñîôèè ñëåäóåòå â ðàçðàáîòêå ÏÎ, êîíå÷íîé öåëüþ ïðèëîæåíèÿ
    âñåãäà áóäåò ïðè¼ì è ðàçáîð çàïðîñà è ñîçäàíèå ñîîòâåòñòâóþùåãî îòâåòà.
    Symfony ñïðîåêòèðîâàí èñõîäÿ èç ýòèõ ðåàëèé.

    Ñîâåò: Äëÿ òîãî ÷òîáû óçíàòü áîëüøå ïðî ñïåöèôèêàöèþ HTTP, ïðî÷èòàéòå îðèãèíàë HTTP 1.1 RFC èëè æå HTTP Bis, êîòîðûé ÿâëÿåòñÿ èíèöèàòèâîé ïî ðàçúÿñíåíèþ
    îðèãèíàëüíîé ñïåöèôèêàöèè. Çàìå÷àòåëüíûé èíñòðóìåíò äëÿ ïðîâåðêè çàãîëîâêîâ çàïðîñà è îòâåòà ïðè ñ¼ðôèíãå - ýòî ðàñøèðåíèå äëÿ Firefox Live HTTP Headers.

    2.1.2 Çàïðîñû è îòâåòû â PHP

    Êàê æå âû îáðàáàòûâàåòå çàïðîñ è ñîçäàåòå îòâåò ïðè èñïîëüçîâàíèè PHP? Íà
    ñàìîì äåëå PHP íåìíîãî àáñòðàãèðóåò âàñ îò ïðîöåññà:
    $uri = $_SERVER['REQUEST_URI'];
    $foo = $_GET['foo'];
    header('Content-type: text/html');
    echo 'The URI requested is: '.$uri;
    echo 'The value of the "foo" parameter is: '.$foo;

    Êàê áû ñòðàííî ýòî íè çâó÷àëî, íî ýòî êðîõîòíîå ïðèëîæåíèå ïîëó÷àåò èíôîðìàöèþ èç
    HTTP-çàïðîñà è èñïîëüçóåò å¼ äëÿ ñîçäàíèÿ HTTP-îòâåòà. Âìåñòî òîãî, ÷òîáû ïàðñèòü
    íåîáðàáîòàííûé HTTP-çàïðîñ, PHP ïîäãîòàâëèâàåò ñóïåðãëîáàëüíûå ïåðåìåííûå, òàêèå êàê $_SERVER è $_GET, êîòîðûå ñîäåðæàò âñþ èíôîðìàöèþ î çàïðîñå. Àíàëîãè÷íî,
    âìåñòî òîãî, ÷òîáû âîçâðàùàòü òåêñò îòâåòà, ôîðìàòèðîâàííûé ïî ïðàâèëàì HTTP,
    âû ìîæåòå èñïîëüçîâàòü ôóíêöèè header() äëÿ ñîçäàíèÿ çàãîëîâêîâ îòâåòîâ è ïðîñòî âûâåñòè íà ïå÷àòü îñíîâíîé êîíòåíò, êîòîðûé ñòàíåò êîíòåíòíûì áëîêîì îòâåòà. Â
    çàêëþ÷åíèè PHP ñîçäàñò ïðàâèëüíûé HTTP-îòâåò è âåðíåò åãî êëèåíòó:
    HTTP/1.1 200 OK
    Date: Sat, 03 Apr 2011 02:14:33 GMT
    Server: Apache/2.2.17 (Unix)
    Content-Type: text/html

    2.1.

    Symfony2 è îñíîâû HTTP

    15

    Symfony Documentation, Âûïóñê 2.0

    The URI requested is: /testing?foo=symfony
    The value of the "foo" parameter is: symfony

    2.1.3 Çàïðîñû è îòâåòû â Symfony

    Symfony ïðåäîñòàâëÿåò àëüòåðíàòèâó ïðÿìîëèíåéíîìó ïîäõîäó èç PHP ïîñðåäñòâîì
    äâóõ êëàññîâ, êîòîðûå ïîçâîëÿþò âçàèìîäåéñòâîâàòü ñ HTTP-çàïðîñîì è îòâåòîì ñàìûì ïðîñòåéøèì ñïîñîáîì. Êëàññ Symfony\Component\HttpFoundation\Request - ýòî
    ïðîñòîå îáúåêòíî-îðèåíòèðîâàííîå ïðåäñòàâëåíèå ñîîáùåíèÿ HTTP-çàïðîñà. Ñ åãî ïîìîùüþ âû èìååòå âñå äàííûå èç çàïðîñà íà êîí÷èêàõ ïàëüöåâ:
    use Symfony\Component\HttpFoundation\Request;
    $request = Request::createFromGlobals();
    // çàïðîøåííûé URI (íà ïðèìåð /about) áåç query parameters
    $request->getPathInfo();
    // ïîëó÷àåì GET è POST ïåðåìåííûå ñîîòâåòñòâåííî
    $request->query->get('foo');
    $request->request->get('bar');
    // ïîëó÷àåì ýêçåìïëÿð UploadedFile îïðåäåëÿåìûé èäåíòèôèêàòîðîì foo
    $request->files->get('foo');
    $request->getMethod();
    $request->getLanguages();

    // GET, POST, PUT, DELETE, HEAD
    // ìàññèâ ÿçûêîâ, ïðèíèìàåìûõ êëèåíòîì

     êà÷åñòâå áîíóñà, êëàññ Request âûïîëíÿåò áîëüøîé îáú¼ì ðàáîòû â ôîíîâîì ðåæèìå, òàê ÷òî âàì íå ïðèäåòñÿ çàáîòèòüñÿ î ìíîãèõ âåùàõ. Íàïðèìåð, ìåòîä isSecure()
    ïðîâåðÿåò òðè ðàçëè÷íûõ çíà÷åíèÿ â PHP, êîòîðûå óêàçûâàþò, ÷òî ïîëüçîâàòåëü ïîäêëþ÷àåòñÿ ïî çàùèùåííîìó ïðîòîêîëó (https).
    Symfony òàêæå ïðåäîñòàâëÿåò êëàññ Response: ïðîñòîå ÐHP-ïðåäñòàâëåíèå HTTPîòâåòà. Ýòî ïîçâîëÿåò âàøåìó ïðèëîæåíèþ èñïîëüçîâàòü îáúåêòíî-îðèåíòèðîâàííûé
    èíòåðôåéñ äëÿ êîíñòðóèðîâàíèÿ îòâåòà, êîòîðûé íóæíî âåðíóòü êëèåíòó:
    use Symfony\Component\HttpFoundation\Response;
    $response = new Response();
    $response->setContent('

    Hello world!

    ');
    $response->setStatusCode(200);
    16

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    $response->headers->set('Content-Type', 'text/html');
    // prints the HTTP headers followed by the content
    $response->send();

    Åñëè áû Symfony íè÷åãî âàì íå ïðåäëàãàëà, âû âñåãäà äîëæíû áûëè áû èìåòü íàáîð
    èíñòðóìåíòîâ äëÿ òîãî ÷òîáû ìîæíî áûëî ïðîñòî è áûñòðî ïîëó÷èòü äîñòóï ê èíôîðìàöèè èç çàïðîñà è îáúåêòíî-îðèåíòèðîâàííûé èíòåðôåéñ äëÿ ñîçäàíèÿ îòâåòà. Äàæå
    åñëè âû îñâîèòå áîëåå ìîùíûå âîçìîæíîñòè â Symfony, âñåãäà äåðæèòå â ãîëîâå, ÷òî
    öåëü âàøåãî ïðèëîæåíèÿ âñåãäà çàêëþ÷àåòñÿ â òîì, ÷òîáû èíòåðïðåòèðîâàòü çàïðîñ
    è ñîçäàòü ñîîòâåòñòâóþùèé îòâåò, îñíîâûâàÿñü íà ëîãèêå âàøåãî ïðèëîæåíèÿ

    Ñîâåò: Êëàññû Request è Response ÿâëÿþòñÿ ÷àñòüþ ñàìîñòîÿòåëüíîãî êîìïîíåíòà
    HttpFoundation. Ýòîò êîìïîíåíò ìîæåò áûòü èñïîëüçîâàí íåçàâèñèìî îò Symfony è îí
    òàêæå ïðåäîñòàâëÿåò êëàññû äëÿ ðàáîòû ñ ñåññèÿìè è çàãðóçêè ôàéëîâ.

    2.1.4 Ïóòåøåñòâèå îò Çàïðîñà äî Îòâåòà

    Êàê è HTTP-ïðîòîêîë, îáúåêòû Request è Response äîñòàòî÷íî ïðîñòû. Ñàìàÿ ñëîæíàÿ ÷àñòü ñîçäàíèÿ ïðèëîæåíèÿ çàêëþ÷àåòñÿ â íàïèñàíèè ïðîöåññîâ, êîòîðûå ïðîèñõîäÿò ìåæäó ïîëó÷åíèåì çàïðîñà è îòïðàâêîé îòâåòà. Äðóãèìè ñëîâàìè, ðåàëüíàÿ ðàáîòà
    çàêëþ÷àåòñÿ â íàïèñàíèè êîäà, êîòîðûé èíòåðïðåòèðóåò èíôîðìàöèþ çàïðîñà è ñîçäàåò
    îòâåò (ëîãèêà ïðèëîæåíèÿ).
    Âàøå ïðèëîæåíèå ìîæåò èìåòü ìíîãî ôóíêöèé, íàïðèìåð, îòïðàâëÿòü email'û, îáðàáàòûâàòü îòïðàâëåííûå ôîðìû, ñîõðàíÿòü ÷òî-òî â áàçó äàííûõ, îòîáðàæàòü HTMLñòðàíèöû è çàùèùàòü êîíòåíò ïðàâèëàìè áåçîïàñíîñòè. Êàê óïðàâëÿòüñÿ ñî âñåì ýòèì
    è ÷òîáû ïðè ýòîì êîä îñòàâàëñÿ õîðîøî îðãàíèçîâàííûì è ïîääåðæèâàåìûì?
    Symfony ñîçäàíà ñïåöèàëüíî äëÿ ðåøåíèÿ ýòèõ ïðîáëåì, çíà÷èò, âàì íå ïðèäåòñÿ èõ
    ðåøàòü.
    Ôðîíò-êîíòðîëëåð

    Òðàäèöèîííî ïðèëîæåíèÿ ñîçäàâàëèñü òàêèì îáðàçîì, ÷òîáû êàæäàÿ ñòðàíèöà èìåëà
    ñâîé ñîáñòâåííûé ôàéë:
    index.php
    contact.php
    blog.php

    Ïðè òàêîì ïîäõîäå èìååòñÿ öåëûé ðÿä ïðîáëåì, âêëþ÷àÿ æ¼ñòêèå URLû (÷òî åñëè âàì
    ïîòðåáóåòñÿ èçìåíèòü blog.php íà news.php è ïðè ýòîì ñîõðàíèòü âñå âàøè ññûëêè?),

    2.1.

    Symfony2 è îñíîâû HTTP

    17

    Symfony Documentation, Âûïóñê 2.0

    à òàêæå íåîáõîäèìîñòü âðó÷íóþ âêëþ÷àòü â êàæäûé ôàéë êó÷ó ôàéëîâ, âêëþ÷àþùèõ
    áåçîïàñíîñòü, ðàáîòó ñ áàçàìè äàííûõ.
    Ìíîãî áîëåå óäà÷íûì ÿâëÿåòñÿ ïîäõîä ñ èñïîëüçîâàíèåì front controller, åäèíñòâåííîãî
    PHP-ôàéëà, êîòîðûé îòâå÷àåò çà êàæäûé çàïðîñ ê âàøåìó ïðèëîæåíèþ. Íàïðèìåð:

    /index.php
    /index.php/contact
    /index.php/blog

    âûïîëíÿåò index.php
    âûïîëíÿåò index.php
    âûïîëíÿåò index.php

    Ñîâåò: Ñ èñïîëüçîâàíèåì ìîäóëÿ mod_rewrite äëÿ Apache (èëè ýêâèâàëåíòà äëÿ äðó-

    ãèõ web-ñåðâåðîâ) URLû ëåãêî î÷èñòèòü îò óïîìèíàíèÿ ôðîíò-êîíòðîëëåðà, ò.å. îñòàíåòñÿ ëèøü /, /contact è /blog.
    Òåïåðü, êàæäûé çàïðîñ îáðàáàòûâàåòñÿ îäíîîáðàçíî. Âìåñòî òîãî ÷òîáû êàæäûé URL
    ñîîòâåòñòâîâàë îòäåëüíîìó PHP-ôàéëó - ôðîíò-êîíòðîëëåð âûïîëíÿåòñÿ âñåãäà è ïîñðåäñòâîì ìàðøðóòèçàòîðà âûçûâàåò ðàçëè÷íûå ÷àñòè âàøåãî ïðèëîæåíèÿ, â çàâèñèìîñòè îò URL. Ýòî ðåøàåò ìíîãèå ïðîáëåìû, êîòîðûå ïîðîæäàë òðàäèöèîííûé ïîäõîä. Ïðàêòè÷åñêè âñå ñîâðåìåííûå ïðèëîæåíèÿ èñïîëüçóþò ýòîò ïîäõîä, íàïðèìåð
    WordPress.
    Áóäüòå îðãàíèçîâàíû

    Èòàê, ìû âíóòðè âàøåãî ôðîíò-êîíòðîëëåðà. Íî êàê ìû óçíàåì, êàêàÿ ñòðàíèöà äîëæíà áûòü îòîáðàæåíà è êàê å¼ ñôîðìèðîâàòü?  ëþáîì ñëó÷àå âàì íóæíî ïðîâåðèòü
    âõîäÿùèé URI è âûïîëíèòü êàêóþ-òî èç ÷àñòåé âàøåãî êîäà, â çàâèñèìîñòè îò ýòîãî
    çíà÷åíèÿ. Ýòî ìîæíî ñäåëàòü áûñòðî è âåñüìà êîðÿâî:
    // index.php
    $request = Request::createFromGlobals();
    $path = $request->getPathInfo(); // çàïðîøåííûé URL
    if (in_array($path, array('', '/')) {
    $response = new Response('Welcome to the homepage.');
    } elseif ($path == '/contact') {
    $response = new Response('Contact us');
    } else {
    $response = new Response('Page not found.', 404);
    }
    $response->send();

    Ðåøèòü æå ýòó ïðîáëåìó äîñòàòî÷íî ñëîæíî. Ê ñ÷àñòüþ, Symfony ñîçäàíà
    ýòîãî.

    18

    èìåííî

    Ãëàâà 2.

    äëÿ

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Êàê óñòðîåíî Symfony ïðèëîæåíèå

    Êîãäà âû äà¼òå âîçìîæíîñòü Symfony îáðàáàòûâàòü çàïðîñû, æèçíü ñòàíîâèòñÿ ìíîãî
    ïðîùå. Symfony ñëåäóåò ïðîñòîìó øàáëîíó ïðè îáðàáîòêå êàæäîãî çàïðîñà:

    Ðèñ. 2.1: Âõîäÿùèå çàïðîñû èíòåðïðåòèðóþòñÿ ìàðøðóòèçàòîðîì è ïåðåäàþòñÿ â
    ôóíêöèþ-êîíòðîëëåð, êîòîðàÿ âîçâðàùàåò îáúåêò Response.
    Êàæäàÿ ñòðàíèöà âàøåãî ñàéòà äîëæíà áûòü îïðåäåëåíà â êîíôèãóðàöèè ìàðøðóòèçàòîðà, ÷òîáû ðàñïðåäåëÿòü ðàçëè÷íûå URL ïî ðàçëè÷íûì PHP-ôóíêöèÿì. Îáÿçàííîñòü êàæäîé òàêîé ôóíêöèè, íàçûâàåìîé controller, èñïîëüçóÿ èíôîðìàöèþ èç çàïðîñà
    - à òàêæå èñïîëüçóÿ ïðî÷èé èíñòðóìåíòàðèé, äîñòóïíûé â Symfony, ñîçäàòü è âåðíóòü
    îáúåêò Response. Äðóãèìè ñëîâàìè, êîíòðîëëåð ñîäåðæèò âàø êîä: èìåííî òàì âû
    äîëæíû ïðåâðàòèòü çàïðîñ â îòâåò.
    Ýòî íå ñëîæíî! Äàâàéòå-êà âçãëÿíåì:
    ˆ Êàæäûé çàïðîñ îáðàáàòûâàåòñÿ ôðîíò-êîíòðîëëåðîì;
    ˆ Ñèñòåìà ìàðøðóòèçàöèè îïðåäåëÿåò, êàêóþ èìåííî PHP-ôóíêöèþ íåîáõîäèìî
    âûïîëíèòü, îñíîâûâàÿñü íà èíôîðìàöèè èç çàïðîñà è êîíôèãóðàöèè ìàðøðóòèçàòîðà, êîòîðóþ âû ñîçäàëè;
    ˆ Âûçûâàåòñÿ íåîáõîäèìàÿ ôóíêöèÿ, â êîòîðîé íàïèñàííûé âàìè êîä ñîçäà¼ò è âîçâðàùàåò ñîîòâåòñòâóþùèé ëîãèêå ïðèëîæåíèÿ îáúåêò Response.
    Symfony Request â äåéñòâèè

    Íå çàêàïûâàÿñü ãëóáîêî â äåòàëè, äàâàéòå ïîñìîòðèì íà ýòîò ïðîöåññ â äåéñòâèè. Ïðåäïîëîæèì, âû õîòèòå äîáàâèòü ñòðàíèöó /contact ê âàøåìó Symfony ïðèëîæåíèþ. Âîïåðâûõ, íàäî äîáàâèòü êîíôèãóðàöèþ ìàðøðóòèçàòîðà äëÿ /contact URI:

    2.1.

    Symfony2 è îñíîâû HTTP

    19

    Symfony Documentation, Âûïóñê 2.0

    contact:
    pattern: /contact
    defaults: { _controller: AcmeDemoBundle:Main:contact }

    Ïðèìå÷àíèå: Ýòîò ïðèìåð èñïîëüçóåò YAML äëÿ òîãî ÷òîáû îïðåäåëèòü êîíôèãóðà-

    öèþ ìàðøðóòèçàòîðà. Êîíôèãóðàöèþ ìîæíî òàêæå çàäàâàòü è â äðóãèõ ôîðìàòàõ òàêèõ êàê XML èëè PHP.

    Êîãäà êòî-ëèáî ïîñåùàåò ñòðàíèöó /contact, URI ñîâïàäàåò ñ ìàðøðóòîì è óêàçàííûé íàìè ðàíåå êîíòðîëëåð âûïîëíÿåòñÿ. Êàê âû óçíàåòå â èç ãëàâû Ìàðøðóòèçàöèÿ ,
    ñòðîêà AcmeDemoBundle:Main:contact ýòî êîðîòêàÿ ôîðìà çàïèñè, êîòîðàÿ óêàçûâàåò
    íà îñîáûé ìåòîä contactAction, îïðåäåë¼ííûé â êëàññå MainController:
    class MainController
    {
    public function contactAction()
    {
    return new Response('

    Contact us!

    ');
    }
    }

     ýòîì î÷åíü ïðîñòîì ïðèìåðå, êîíòðîëëåð ñîçäàåò îáúåêò Response, ñîäåðæàùèé ëèøü
    ïðîñòåíüêèé HTML-êîä 

    Contact us!

    . Â ãëàâå Êîíòðîëëåð , âû óçíàåòå, êàê
    êîíòðîëëåð ìîæåò îòîáðàæàòü øàáëîíû, ïîçâîëÿÿ ïðåäñòàâëåíèþ ñóùåñòâîâàòü ðàçäåëüíî îò êîäà â ôàéëàõ øàáëîíîâ. Ýòî äàåò âîçìîæíîñòü ñîñðåäîòî÷èòüñÿ â êîíòðîëëåðå íà ðàáîòå ñ áàçàìè äàííûõ, îáðàáîòêå îòïðàâëåííûõ ïîëüçîâàòåëåì äàííûõ èëè
    îòïðàâêå email ñîîáùåíèé.
    2.1.5 Symfony2: Ñîçäàâàéòå ïðèëîæåíèå, à íå èíñòðóìåíòû.

    Òåïåðü âû çíàåòå, ÷òî öåëü âàøåãî ïðèëîæåíèÿ çàêëþ÷àåòñÿ â èíòåðïðåòàöèè âõîäÿùèõ
    çàïðîñîâ è ñîçäàíèè àäåêâàòíîãî ñèòóàöèè îòâåòà. Ïî ìåðå ðîñòà ïðèëîæåíèÿ ñòàíîâèòñÿ âñå òðóäíåå ñîäåðæàòü ñâîé êîä â ïîðÿäêå. Áåç ñîìíåíèé, ýòà æå çàäà÷à áóäåò
    ïîâòîðÿòüñÿ ñíîâà è ñíîâà: ñîõðàíåíèå äàííûõ â áàçó, îòîáðàæåíèå è ïîâòîðíîå èñïîëüçîâàíèå øàáëîíîâ, îáðàáîòêà ôîðì, îòïðàâêà emails, âàëèäàöèÿ äàííûõ, ââåä¼ííûõ
    ïîëüçîâàòåëåì è áåçîïàñíîñòü.
    Õîðîøèå íîâîñòè çàêëþ÷àþòñÿ â òîì, ÷òî ýòè ïðîáëåìû íå óíèêàëüíû. Symfony ïðåäîñòàâëÿåò Ôðåéìâîðê, ïîëíûé èíñòðóìåíòîâ, êîòîðûå ïîçâîëÿò âàì ñîçäàòü âàøå ñîáñòâåííîå ïðèëîæåíèå, à íå âàøè èíñòðóìåíòû. Ïðè ïîìîùè Symfony2 âû èñïîëüçîâàòü
    Ôðåéìâîðê öåëèêîì èëè æå òîëüêî åãî ÷àñòü.

    20

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Àâòîíîìíûå áèáëèîòåêè:

    Êîìïîíåíòû

    Symfony2

    ×òî æå ñîáîé ïðåäñòàâëÿåò Symfony2? Ïðåæäå âñåãî, Symfony2 - ýòî êîëëåêöèÿ áîëåå ÷åì 20 íåçàâèñèìûõ áèáëèîòåê, êîòîðûå ìîãóò áûòü èñïîëüçîâàíû â ëþáîì PHPïðîåêòå. Ýòè áèáëèîòåêè, íàçûâàåìûå Symfony2 Components, ñîäåðæàò ïîëåçíûå ìåòîäû ïðàêòè÷åñêè íà ëþáîé ñëó÷àé æèçíè, íå çàâèñèìî îò òîãî êàê èìåííî âàø ïðîåêò
    ðàçðàáàòûâàåòñÿ. Âîò íåêîòîðûå èç íèõ:
    ˆ HttpFoundation - Ñîäåðæèò êëàññû Request è Response, à òàêæå êëàññû äëÿ ðàáîòû ñ ñåññèÿìè è çàãðóçêîé ôàéëîâ;
    ˆ Routing - ìîùíàÿ ñèñòåìà ìàðøðóòèçàöèè, êîòîðàÿ ïîçâîëÿåò âàì ñòàâèòü â ñîîòâåòñòâèå íåêîòîðîìó URI (íàïðèìåð /contact) èíôîðìàöèþ î òîì, êàê ýòîò
    çàïðîñ äîëæåí áûòü îáðàáîòàí (íàïðèìåð âûçâàòü ìåòîä contactAction());
    ˆ Form - ìíîãîôóíêöèîíàëüíûé è ãèáêèé ôðåéìâîðê äëÿ ñîçäàíèÿ ôîðì îáðàáîòêè
    èõ ñàáìèòà;
    ˆ Validator - ñèñòåìà, ïðåäíàçíà÷åííàÿ äëÿ ñîçäàíèÿ ïðàâèë äëÿ äàííûõ è ïîñëåäóþùåé âàëèäàöèè - ñîîòâåòñòâóþò ëè äàííûå, îòïðàâëåííûå ïîëüçîâàòåëÿìè ýòèì
    ïðàâèëàì;
    ˆ ClassLoader - áèáëèîòåêà, ïîçâîëÿþùàÿ èñïîëüçîâàòü PHP-êëàññû áåç èñïîëüçîâàíèÿ ÿâíîãî require äëÿ ôàéëîâ, âêëþ÷àþùèõ òðåáóåìûå êëàññû.
    ˆ Templating - òóëêèò äëÿ ðåíäåðèíãà øàáëîíîâ, ïîääåðæèâàåò íàñëåäîâàíèå øàáëîíîâ (íàïðèìåð, äåêîðèðîâàíèå øàáëîíîâ ïðè ïîìîùè ðîäèòåëüñêîãî øàáëîíà
    aka layout), à òàêæå ïðî÷èå òèïè÷íûå äëÿ øàáëîíîâ îïåðàöèè (escaping, óñëîâèÿ,
    öèêëû è ò.ä.);
    ˆ Security ìîùíàÿ áèáëèîòåêà äëÿ îáåñïå÷åíèÿ âñåõ òèïîâ áåçîïàñíîñòè âíóòðè ïðèëîæåíèÿ;
    ˆ Translation - Ôðåéìâîðê äëÿ ïîääåðæêè ïåðåâîäîâ â âàøåì ïðèëîæåíèè.
    Êàæäûé èç ýòèõ êîìïîíåíòîâ íåçàâèñèì è ìîæåò áûòü èñïîëüçîâàí â
    ïðîåêòå, íå çàâèñèìî îò Symfony2.
    Êîìïëåêñíîå ðåøåíèå: Symfony2

    ëþáîì

    PHP-

    Framework

    Íó òàê ÷òî æå ýòî òàêîå - Symfony2 Framework ? Symfony2
    òåêà, êîòîðàÿ ðåøàåò 2 ðàçëè÷íûõ çàäà÷è:

    Framework

    ýòî PHP áèáëèî-

    1. Ïðåäîñòàâëÿåò íàáîð îòîáðàííûõ êîìïîíåíò (Symfony2 Components) è ñòîðîííèõ
    áèáëèîòåê (íàïðèìåð Swiftmailer äëÿ îòïðàâêè ïî÷òû);
    2. Ïðåäîñòàâëÿåò âîçìîæíîñòè ïî êîíôèãóðèðîâàíèþ âñåãî ýòîãî äîáðà è êëåé,
    êîòîðûé ñêðåïëÿåò âñå áèáëèîòåêè â åäèíîå öåëîå.

    2.1.

    Symfony2 è îñíîâû HTTP

    21

    Symfony Documentation, Âûïóñê 2.0

    Öåëü ôðåéìâîðêà - èíòåãðàöèÿ íåçàâèñèìûõ èíñòðóìåíòîâ è îáåñïå÷åíèå èõ ñîâìåñòíîé
    ðàáîòû. Ñàì ôðåéìâîðê ïðåäñòàâëÿåò ñîáîé Symfony Bundle (ïëàãèí), êîòîðûé ìîæíî
    êîíôèãóðèðîâàòü èëè äàæå çàìåíèòü.
    Symfony2 ïðåäîñòàâëÿåò çàìå÷àòåëüíûé íàáîð èíñòðóìåíòîâ äëÿ áûñòðîé ðàçðàáîòêè
    web-ïðèëîæåíèé, íè÷åãî íå íàâÿçûâàþùèé íåïîñðåäñòâåííî âàøåìó ïðèëîæåíèþ. Ðàçðàáîò÷èê ìîæåò áûñòðî ïðèñòóïèòü ê ðàçðàáîòêå, èñïîëüçóÿ äèñòðèáóòèâ Symfony2,
    êîòîðûé ïðåäîñòàâëÿåò ñêåëåòîí ñ òèïîâûìè íàñòðîéêàìè. À äëÿ ïûòëèâûõ óìîâ... ó
    íåáà íåò ïîòîëêà! )

    2.2 Symfony2 ïðîòèâ ÷èñòîãî PHP

    Ïî÷åìó èñïîëüçîâàòü Symfony2 ëó÷øå, ÷åì ïðîñòîé PHP ôàéë, êîòîðûé
    ìîæíî ïðîñòî îòêðûòü è ïèñàòü êîä íå çàäóìûâàÿñü?
    Åñëè ðàíüøå âû íèêîãäà íå ïîëüçîâàëèñü PHP-ôðåéìâîðêàìè, íå çíàêîìû ñ ôèëîñîôèåé Model-View-Controller (çäåñü è äàëåå MVC) èëè æå óäèâëåíû ñóìàòîõîé âîêðóã
    Symfony2, òî ýòà ãëàâà ñîçäàíà ñïåöèàëüíî äëÿ âàñ! Âìåñòî òîãî ÷òîáû ðàññêàçàòü âàì
    î òîì, ÷òî Symfony2 ïîçâîëèò ðàçðàáàòûâàòü áûñòðåå è êà÷åñòâåííåå, ÷åì ïðè èñïîëüçîâàíèè ÷èñòîãî PHP, ìû ïðîñòî ïîêàæåì âàì ýòî.
     ýòîé ãëàâå, âû ñîçäàäèòå ïðîñòåíüêîå ïðèëîæåíèå íà ÷èñòîì PHP è âûïîëíèòå åãî
    îïòèìèçàöèþ. Âû ñîâåðøèòå ñâîåîáðàçíîå ïóòåøåñòâèå ñâîçü âðåìÿ, íàáëþäàÿ çà ðàçâèòèåì web-ðàçðàáîòêè íà ïðîòÿæåíèè ïîñëåäíèõ ëåò îò ïëîñêîãî PHP äî ñåãîäíÿøíåãî
    óðîâíÿ.
    Â êîíöå êîíöîâ, âû óâèäèòå, êàê Symfony2 ïîìîæåò âàì èçáàâèòüñÿ îò ðóòèííûõ çàäà÷
    è âåðíóòü âàì êîíòðîëü íàä êîäîì.
    2.2.1 Ïðîñòîé áëîã íà ÷èñòîì PHP

     ýòîé ãëàâå âû ñîçäàäèòå ïðîñòîå ïðèëîæåíèå - áëîã, èñïîëüçóÿ ëèøü îáû÷íûé PHP.
    ×òîáû íà÷àòü, ñîçäàéòå ñòðàíèöó, êîòîðàÿ îòîáðàæàåò çàïèñè â áëîãå, êîòîðûå áûëè
    ñîõðàíåíû â áàçå äàííûõ. Ïèñàòü íà ÷èñòîì PHP ïðîùå ïðîñòîãî:
    // index.php
    $link = mysql_connect('localhost', 'myuser', 'mypassword');
    mysql_select_db('blog_db', $link);
    $result = mysql_query('SELECT id, title FROM post', $link);
    ?>

    22

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0


    List of Posts


    List of Posts





    mysql_close($link);

    Òàêîé êîä áûñòðî ïèøåòñÿ, òàêæå áûñòðî âûïîëíÿåòñÿ, è, ïî ìåðå ðîñòà âàøåãî
    ïðèëîæåíèÿ, ñòàíîâèòñÿ ñîâåðøåííî íåïîääåðæèâàåìûì. Òàêèì îáðàçîì, òóò èìååòñÿ
    íåñêîëüêî ïðîáëåì, êîòîðûå òðåáóåòñÿ ðåøèòü:
    ˆ Íåò îáðàáîò÷èêà îøèáîê: À ÷òî, åñëè ïîäêëþ÷åíèå ê áàçå äàííûõ îòâàëèòñÿ?
    ˆ Ïëîõàÿ îðãàíèçàöèÿ êîäà: Ïî ìåðå ðîñòà ïðèëîæåíèÿ, ýòîò ôàéë áóäåò âñå
    áîëüøå è áîëüøå, â òî æå âðåìÿ ïîääåðæèâàòü åãî áóäåò âñ¼ ñëîæíåå è ñëîæíåå.
    Ãäå âû äîëæíû áóäåòå ðàçìåñòèòü êîä, êîòîðûé îáðàáàòûâàåò îòïðàâêó ôîðìû?
    Êàê âû áóäåòå ïðîâåðÿòü âõîäíûå äàííûå? À êóäà ðàçìåñòèòü êîä äëÿ îòïðàâêè
    email'îâ?
    ˆ Ñëîæíîñòü (à ñêîðåå äàæå íåâîçìîæíîñòü) ïîâòîðíîãî èñïîëüçîâàíèÿ
    êîäà: Òàê êàê âåñü êîä ðàñïîëàãàåòñÿ â îäíîì ôàéëå, íåò íèêàêîé âîçìîæíîñòè
    ïîâòîðíîãî èñïîëüçîâàíèÿ ëþáîé ÷àñòè ïðèëîæåíèÿ äëÿ äðóãèõ ñòðàíèö áëîãà.

    Ïðèìå÷àíèå: Äðóãàÿ ïðîáëåìà, íå óïîìÿíóòàÿ âûøå, çàêëþ÷àåòñÿ â òîì, ÷òî âû
    ôàêòè÷åñêè ïðèâÿçàíû ê áàçå äàííûõ MySQL.  äàííîé ãëàâå ýòîò âîïðîñ íå ðàññìàòðèâàåòñÿ, íî, òåì íå ìåíåå, Symfony2 èçíà÷àëüíî èíòåãðèðîâàíà ñ ORM Doctrine,
    áèáëèîòåêîé, îòâå÷àþùåé çà àáñòðàêöèþ îò áàç äàííûõ è ñîîòâåòñòâèå äàííûõ ìåæäó
    ÑÓÁÄ è âàøèìè ñóùíîñòÿìè (mapping).
    Äàâàéòå æå ïîðàáîòàåì íàä ðàçðåøåíèåì ïîñòàâëåííûõ âûøå ïðîáëåì.

    2.2.

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    23

    Symfony Documentation, Âûïóñê 2.0

    Èçîëÿöèÿ ïðåäñòàâëåíèÿ

    Ïðè ðàçäåëåíèè ëîãèêè ïðèëîæåíèÿ îò êîäà, êîòîðûé ïîäãîòàâëèâàåò HTML ïðåäñòàâëåíèå ñòðàíèöû - îáùàÿ ñòðóêòóðà ïðèëîæåíèÿ ñðàçó æå âûèãðûâàåò:
    // index.php
    $link = mysql_connect('localhost', 'myuser', 'mypassword');
    mysql_select_db('blog_db', $link);
    $result = mysql_query('SELECT id, title FROM post', $link);
    $posts = array();
    while ($row = mysql_fetch_assoc($result)) {
    $posts[] = $row;
    }
    mysql_close($link);
    // include the HTML presentation code
    require 'templates/list.php';

    HTML êîä òåïåðü ðàñïîëîæåí â îòäåëüíîì ôàéëå (templates/list.php), êîòîðûé ãëàâíûì îáðàçîì ïðåäñòàâëÿåò ñîáîé HTML-ôàéë, êîòîðûé èñïîëüçóåò PHP-ñèíòàêñèñ äëÿ
    øàáëîíîâ:


    List of Posts


    List of Posts






    Ïî äîãîâîð¼ííîñòè, ôàéë, êîòîðûé ñîäåðæèò âñþ ëîãèêó ïðèëîæåíèÿ - index.php íàçûâàåòñÿ êîíòðîëëåð. Òåðìèí controller - ýòî ñëîâî, êîòîðîå âû áóäåòå ÷àñòåíüêî
    ñëûøàòü âíå çàâèñèìîñòè îò ÿçûêà ïðîãðàììèðîâàíèÿ èëè æå ôðåéìâîðêà, êîòîðûé
    24

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    èñïîëüçóåòå.  äåéñòâèòåëüíîñòè æå, ðå÷ü èä¼ò î ÷àñòè
    òûâàåò ïîëüçîâàòåëüñêèé ââîä è ãîòîâèò îòâåò.

    âàøåãî

    êîäà, êîòîðûé îáðàáà-

     íàøåì ñëó÷àå, êîíòðîëëåð ïîëó÷àåò äàííûå èç áàçû è ïîäêëþ÷àåò øàáëîí, äëÿ òîãî
    ÷òîáû îòîáðàçèòü èõ. Ñ èçîëÿöèåé êîíòðîëëåðà, âû ïîëó÷èëè âîçìîæíîñòü ïîìåíÿòü
    ëèøü øàáëîí, åñëè âàì âäðóã ïîíàäîáèòñÿ îòîáðàçèòü çàïèñè áëîãà â äðóãîì ôîðìàòå
    (íàïðèìåð list.json.php äëÿ èñïîëüçîâàíèÿ JSON-ôîðìàòà).
    Èçîëÿöèÿ ëîãèêè Ïðèëîæåíèÿ (Äîìåíà)

    Ïîêà íàøå ïðèëîæåíèå ñîäåðæàëî âñåãî îäíó ñòðàíèöó. Íî ÷òî æå äåëàòü, åñëè íóæíî
    äîáàâèòü âòîðóþ ñòðàíèöó, êîòîðàÿ èñïîëüçóåò òî æå ïîäêëþ÷åíèå ê áàçå äàííûõ èëè
    äàæå òîò æå ìàññèâ ïîñòîâ èç áëîãà? Äàâàéòå ïðåîáðàçóåì êîä, èçîëèðîâàâ áàçîâóþ
    ëîãèêó îò ôóíêöèé äîñòóïà ê ÁÄ - ïîìåñòèì èõ â íîâûé ôàéë ïîä íàçâàíèåì model.php:
    // model.php
    function open_database_connection()
    {
    $link = mysql_connect('localhost', 'myuser', 'mypassword');
    mysql_select_db('blog_db', $link);
    }

    return $link;

    function close_database_connection($link)
    {
    mysql_close($link);
    }
    function get_all_posts()
    {
    $link = open_database_connection();
    $result = mysql_query('SELECT id, title FROM post', $link);
    $posts = array();
    while ($row = mysql_fetch_assoc($result)) {
    $posts[] = $row;
    }
    close_database_connection($link);
    }

    2.2.

    return $posts;

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    25

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Èìÿ ôàéëà model.php èñïîëüçîâàíî íå ñëó÷àéíî - ëîãèêà è äîñòóï ê äàííûì
    ïðèëîæåíèÿ òðàäèöèîííî èçâåñòåí êàê óðîâåíü ìîäåëè. Â ïðàâèëüíî îðãàíèçîâàííîì
    ïðèëîæåíèè áÎëüøàÿ ÷àñòü êîäà ïðåäñòàâëÿþùàÿ ñîáîé áèçíåñ-ëîãèêó äîëæíà áûòü
    ðàñïîëîæåíà â ìîäåëè (â ïðîòèâîâåñ ðàñïîëîæåíèþ å¼ â êîíòðîëëåðå). È, â îòëè÷èå îò
    íàøåãî ïðèìåðà, ëèøü ÷àñòü ìîäåëè îòâå÷àåò çà äîñòóï ê ÁÄ (à áûâàåò è âîîáùå íå
    îòâå÷àåò).
    Êîíòðîëëåð (index.php) òåïåðü âûãëÿäèò î÷åíü ïðîñòî:
    require_once 'model.php';
    $posts = get_all_posts();
    require 'templates/list.php';

    Òåïåðü, â îáÿçàííîñòè êîíòðîëëåðà âìåíÿåòñÿ ïîëó÷åíèå äàííûõ èç ìîäåëè ïðèëîæåíèÿ
    è âûçîâ øàáëîíà äëÿ îòîáðàæåíèÿ äàííûõ. Ýòî î÷åíü ïðîñòîé ïðèìåð ïàòòåðíà modelview-controller.
    Èçîëÿöèÿ ðàçìåòêè (Layout)

    Íà òåêóùèé ìîìåíò, ïðèëîæåíèå ðàçäåëåíî íà òðè ðàçëè÷íûõ ÷àñòè, ïðåäëàãàþùèõ
    ðàçëè÷íûå ïðåèìóùåñòâà è âîçìîæíîñòè ïî ïîâòîðíîìó èñïîëüçîâàíèþ ïî÷òè ëþáîãî
    êîäà äëÿ äðóãèõ ñòðàíèö.
    Ïîêà ÷òî ìû íå ìîæåì ïîâòîðíî èñïîëüçîâàòü - ýòî ðàçìåòêà ñòðàíèöû (layout). Èñïðàâèì ýòî óïóùåíèå, ñîçäàâ ôàéë layout.php:



    <?php echo $title ?>






    Øàáëîí (templates/list.php) ìîæåò áûòü óïðîù¼í, òàê êàê áóäåò ðàñøèðÿòü áàçîâóþ ðàçìåòêó:


    List of Posts



      26

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0












    Òåïåðü âû çíàåòå ìåòîäîëîãèþ, êîòîðàÿ ïîçâîëÿåò ïîâòîðíî èñïîëüçîâàòü ðàçìåòêólayout. Ê ñîæàëåíèþ, äëÿ òîãî ÷òîáû äîñòè÷ü ýòîãî, âû âûíóæäåíû èñïîëüçîâàòü
    íåñêîëüêî ñòðàøíåíüêèõ PHP-ôóíêöèé (ob_start(), ob_get_clean()) â øàáëîíå.
    Symfony2 èñïîëüçóåò êîìïîíåíò Templating, êîòîðûé ïîçâîëÿåò äîñòè÷ü ýòîãî ïðîñòî
    è ïðîçðà÷íî. Ñêîðî âû óâèäèòå - êàê èìåííî.
    2.2.2 Äîáàâëÿåì ñòðàíèöó áëîãà show

    Ñòðàíèöà áëîãà list áûëà îïòèìèçèðîâàíà òàêèì îáðàçîì, ÷òîáû êîä áûë ëó÷øå îðãàíèçîâàí è ïîçâîëÿë ïîâòîðíîå èñïîëüçîâàíèå. Äëÿ òîãî ÷òîáû äîêàçàòü, ÷òî âñå îïòèìèçàöèè áûëè íå çðÿ, äîáàâèì ñòðàíèöó show, êîòîðàÿ îòîáðàæàåò îäèí ïîñò èäåíòèôèöèðóåìûé ïî ïàðàìåòðó çàïðîñà - id.
    Äëÿ íà÷àëà, ñîçäàäèì íîâóþ ôóíêöèþ â ôàéëå model.php, êîòîðàÿ ïîëó÷àåò îäèíî÷íóþ
    çàïèñü ïî å¼ id:
    Äàëåå, ñîçäàäèì íîâûé ôàéë, êîòîðûé íàçîâåì show.php - êîíòðîëëåð äëÿ íàøåé íîâîé
    ñòðàíèöû:
    require_once 'model.php';
    $post = get_post_by_id($_GET['id']);
    require 'templates/show.php';

    È, íàêîíåö, ñîçäàäèì íîâûé øàáëîí - templates/show.php - äëÿ îòîáðàæåíèÿ îäíîãî
    ïîñòà èç áëîãà:




    2.2.

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    27

    Symfony Documentation, Âûïóñê 2.0







    Ñîçäàíèå âòîðîé ñòðàíèöû âûïîëíåíî ëåãêî è íåïðèíóæäåííî, è ìû èçáåæàëè äóáëèðîâàíèÿ êîäà. Òåì íå ìåíåå, ýòà ñòðàíèöà äîáàâëÿåò äàæå áîëüøå ïðîáëåì, êîòîðûå
    ôðåéìâîðê ìîæåò ðåøèòü äëÿ âàñ. Íàïðèìåð, îòñóòñòâóþùèé èëè íåâåðíûé ïàðàìåòð
    id âûçîâåò ôàòàëüíóþ îøèáêó ïðèëîæåíèÿ. Áûëî áû ëó÷øå, åñëè áû â ýòîì ñëó÷àå
    îòîáðàæàëàñü ñòðàíèöà 404, íî ñåé÷àñ ìû íå ìîæåì ëåãêî äîñòè÷ü òàêîãî ýôôåêòà.
    È åù¼ ëîæêà ä¼ãòÿ - âåäü âû çàáûëè î÷èñòèòü ïàðàìåòð id ïðè ïîìîùè ôóíêöèè
    mysql_real_escape_string() - òàê ÷òî âñÿ âàøà áàçà äàííûõ ïîäâåðãàåòñÿ ðèñêó SQLèíúåêöèè.
    Äðóãàÿ ñåðü¼çíàÿ ïðîáëåìà çàêëþ÷àåòñÿ â òîì, ÷òî êàæäûé ôàéë-êîíòðîëëåð äîëæåí
    ïîäêëþ÷àòü ôàéë model.php. À ÷òî åñëè ê êàæäîìó êîíòðîëëåðó íåîæèäàííî ïðèäåòñÿ ïîäêëþ÷èòü äîïîëíèòåëüíûé ôàéë èëè æå âûïîëíèòü äðóãóþ ãëîáàëüíóþ îïåðàöèþ
    (íàïðèìåð, ñâÿçàííóþ ñ áåçîïàñíîñòüþ)? Ïðè íûíåøíåé îðãàíèçàöèè, ýòîò êîä íåîáõîäèìî äîáàâèòü â êàæäûé êîíòðîëëåð. Åñëè âû çàáóäåòå âêëþ÷èòü ÷òî-íèáóäü â îäèí èç
    ôàéëîâ, îñòà¼òñÿ ëèøü íàäåÿòüñÿ, ÷òî ýòî íå ñêàæåòñÿ íà áåçîïàñíîñòè ïðèëîæåíèÿ...
    2.2.3 Front Controller âàì â ïîìîùü

    Ðåøåíèå óêàçàííûõ âûøå ïðîáëåì ÿâëÿåòñÿ èñïîëüçîâàíèå front controller : åäèíñòâåííîãî PHP-ôàéëà, êîòîðûé áóäåò îáðàáàòûâàòü ëþáîé çàïðîñ. Ïðè èñïîëüçîâàíèè front
    controller (äàëåå ïðîñòî ôðîíò-êîíòðîëëåð) URI äëÿ âàøåãî ïðèëîæåíèÿ èçìåíÿþòñÿ
    íåçíà÷èòåëüíî, íî ñòàíîâÿòñÿ áîëåå ãèáêèìè:
    Áåç ôðîíò-êîíòðîëëåðà
    /index.php
    => Ñïèñîê ïîñòîâ (âûïîëíÿåòñÿ index.php)
    /show.php
    => Îòäåëüíûé ïîñò (âûïîëíÿåòñÿ show.php)
    Ïðè èñïîëüçîâàíèè index.php â êà÷åñòâå ôðîíò-êîíòðîëëåðà
    /index.php
    => Ñïèñîê ïîñòîâ (âûïîëíÿåòñÿ index.php)
    /index.php/show
    => Îòäåëüíûé ïîñò (âûïîëíÿåòñÿ index.php)

    Ïðèìå÷àíèå: ×àñòü URI, âêëþ÷àþùàÿ index.php, ìîæåò áûòü îïóùåíà, ïðè èñïîëü-

    çîâàíèè rewrite rules âåá-ñåðâåðà Apache (èëè èõ ýêâèâàëåíòà äëÿ ïðî÷èõ âåá-ñåðâåðîâ).
     ýòîì ñëó÷àå ðåçóëüòèðóþùèé URI äëÿ ñòðàíèöû ñ ïîñòîì áëîãà áóäåò ïðîñòî /show.
    Ïðè èñïîëüçîâàíèè ôðîíò-êîíòðîëëåðà, îäèí PHP ôàéë (index.php â íàøåì ñëó÷àå)
    îáðàáàòûâàåò ëþáîé çàïðîñ. Äëÿ ñòðàíèöû ñ îäíèì ïîñòîì /index.php/show áóäåò âû28

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ïîëíÿòü ôàéë index.php, êîòîðûé òåïåðü íåñ¼ò îòâåòñòâåííîñòü çà ìàðøðóòèçàöèþ
    çàïðîñà, îñíîâûâàÿñü íà ïîëíîì URI. Êàê âû ñêîðî óâèäèòå ôðîíò-êîíòðîëëåð - ýòî
    î÷åíü ìîùíûé èíñòðóìåíò.
    Ñîçäàíèå ôðîíò-êîíòðîëëåðà

    Âíèìàíèå! Ïðÿìî ñåé÷àñ âû ñòîèòå íà ïîðîãå áîëüøîãî øàãà äëÿ âàøåãî ïðèëîæåíèÿ. Èìåÿ îäèí ôàéë, êîòîðûé ïðèíèìàåò âñå çàïðîñû, âû ìîæåòå öåíòðàëèçîâàííî
    îáðàáàòûâàòü âîïðîñû, ñâÿçàííûå, ê ïðèìåðó, ñ áåçîïàñíîñòüþ, çàãðóçêîé êîíôèãóðàöèè, ìàðøðóòèçàöèåé.  íàøåì ïðèëîæåíèè index.php òåïåðü äîëæåí áûòü äîñòàòî÷íî
    óì¼í, ÷òîáû îòîáðàçèòü ñòðàíèöó ñî ñïèñêîì ïîñòîâ èëè ñòðàíèöó îòåëüíîãî ïîñòà, îñíîâûâàÿñü íà URI çàïðîñà:
    // index.php
    // Çàãðóæàåì è èíèöèàëèçèðóåì ãëîáàëüíûå áèáëèîòåêè
    require_once 'model.php';
    require_once 'controllers.php';
    // Âíóòðåííÿÿ ìàðøðóòèçàöèÿ
    $uri = $_SERVER['REQUEST_URI'];
    if ($uri == '/index.php') {
    list_action();
    } elseif ($uri == '/index.php/show' && isset($_GET['id'])) {
    show_action($_GET['id']);
    } else {
    header('Status: 404 Not Found');
    echo '

    Page Not Found

    ';
    }

    Äëÿ óëó÷øåíèÿ ñòðóêòóðû ïðèëîæåíèÿ, îáà êîíòðîëëåðà (ðàíåå index.php è show.php)
    ïðåâðàòèëèñü â ôóíêöèè, è êàæäàÿ èç íèõ áûëà ïîìåùåíà â ôàéë controllers.php:
    // controllers.php
    function list_action()
    {
    $posts = get_all_posts();
    require 'templates/list.php';
    }
    function show_action($id)
    {
    $post = get_post_by_id($id);

    2.2.

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    29

    Symfony Documentation, Âûïóñê 2.0

    }

    require 'templates/show.php';

    Ñòàâ ôðîíò-êîíòðîëëåðîì index.php ïîëó÷èë ñîâåðøåííî íîâóþ ðîëü, âêëþ÷àÿ çàãðóçêó áèáëèîòåê ÿäðà è ìàðøðóòèçàöèþ, êîòîðàÿ ñåé÷àñ çàêëþ÷àåòñÿ â âûçîâå îäíîãî
    èç äâóõ êîíòðîëëåðîâ (ôóíêöèè list_action() è show_action()). Íà ñàìîì äåëå, ýòîò
    ôðîíò-êîíòðîëëåð óæå, â ïëàíå îáðàáîòêè çàïðîñîâ è ìàðøðóòèçàöèè, íà÷èíàåò ñåáÿ
    âåñòè ñõîäíûì îáðàçîì, êàê è êîíòðîëëåð Symfony2.

    Ïðèìå÷àíèå: Äðóãîå äîñòîèíñòâî ôðîíò-êîíòðîëëåðà - ýòî ãèáêèå URL. Îáðàòèòå

    âíèìàíèå, ÷òî URL äëÿ ñòðàíèöû, îòîáðàæàþùåé îòäåëüíûé ïîñò áëîãà, â ëþáîé ìîìåíò ìîæåò áûòü èçìåí¼í ñ /show íà /read, èçìåíèâ êîä âñåãî ëèøü â îäíîì ìåñòå.
    Ðàíåå æå íàì áû ïîòðåáîâàëîñü ïåðåèìåíîâàòü ôàéë öåëèêîì. Â Symfony2 URLû åù¼
    áîëåå ãèáêè.

    Ê ýòîìó âðåìåíè, ïðèëîæåíèå ðàçðîñëîñü ñ îäíîãî PHP-ôàéëà äî öåëîé ñòðóêòóðû,
    êîòîðàÿ õîðîøî îðãàíèçîâàíà è ïîçâîëÿåò ïîâòîðíîå èñïîëüçîâàíèå êîäà. Âû äîëæíû
    áûòü ñ÷àñòëèâû, íî äî ïîëíîãî óäîâëåòâîðåíèÿ åù¼ äàëåêî. Ê ïðèìåðó, ñèñòåìà ìàðøðóòèçàöèè íåíàä¼æíà è íå ìîæåò îïðåäåëèòü, ÷òî ñòðàíèöà list (/index.php) äîëæíà
    áûòü äîñòóïíà ÷åðåç / (åñëè èñïîëüçóþòñÿ Apache rewrite rules). Òàêæå, âìåñòî òîãî
    ÷òîáû ðàçðàáàòûâàòü áëîã, êó÷à âðåìåíè áûëà ïîòðà÷åíà íà àðõèòåêòóðó êîäà (íàïðèìåð, ìàðøðóòèçàöèÿ, âûçîâû êîíòðîëëåðîâ, øàáëîíû è ò.ï.). Åùå áîëüøå âðåìåíè
    íóæíî, ÷òîáû îáðàáàòûâàòü îòïðàâêó ôîðì, âàëèäàöèþ ââåä¼ííûõ äàííûõ, ëîããèðîâàíèå è áåçîïàñíîñòü. Ïî÷åìó ìû äîëæíû çàíîâî èçîáðåòàòü ðåøåíèÿ äëÿ ýòèõ ðóòèííûõ
    ïðîáëåì?
    Ïðèêîñíîâåíèå ê Symfony2

    Symfony2 èä¼ò íà ïîìîùü. Ïåðåä òåì, êàê íà÷àòü èñïîëüçîâàòü Symfony2, âàì íóæíî
    óêàçàòü PHP êàê è ãäå íàéòè êëàññû Symfony2. Ýòî äîñòèãàåòñÿ ïóò¼ì èñïîëüçîâàíèÿ
    àâòîçàãðóç÷èêà, êîòîðûé ïðåäîñòàâëÿåò Symfony. Àâòîçàãðóç÷èê - ýòî èíñòðóìåíò, êîòîðûé ïîçâîëÿåò èñïîëüçîâàòü PHP-êëàññû, íå ïîäêëþ÷àÿ ôàéëû èõ ñîäåðæàùèå ÿâíî.
    Âî-ïåðâûõ, ñêà÷àòü symfony è ïîìåñòèòå ôàéëû â äèðåêòîðèþ vendor/symfony/. Çàòåì,
    ñîçäàéòå ôàéë app/bootstrap.php. Èñïîëüçóéòå åãî äëÿ ïîäêëþ÷åíèÿ (require) äâóõ
    ôàéëîâ ïðèëîæåíèÿ è êîíôèãóðèðîâàíèÿ àâòîçàãðóç÷èêà:
    // bootstrap.php
    require_once 'model.php';
    require_once 'controllers.php';
    require_once 'vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
    $loader = new Symfony\Component\ClassLoader\UniversalClassLoader();

    30

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    $loader->registerNamespaces(array(
    'Symfony' => __DIR__.'/vendor/symfony/src',
    ));
    $loader->register();

    Ýòî ïîêàæåò àâòîçàãðóç÷èêó, ãäå æèâóò êëàññû Symfony. Òåïåðü âû ìîæåòå íà÷àòü
    ïîëüçîâàòüñÿ êëàññàìè Symfony, íå èñïîëüçóÿ îïåðàòîð require äëÿ ôàéëîâ, ñîäåðæàùèõ òðåáóåìûå êëàññû.
    ßäðîì ôèëîñîôèè Symfony ÿâëÿåòñÿ èäåÿ, ÷òî îñíîâíàÿ çàäà÷à ïðèëîæåíèÿ - ýòî èíòåðïðåòèðîâàòü êàæäûé çàïðîñ è âîçâðàòèòü îòâåò. Äëÿ ýòîãî
    Symfony2 ïðåäîñòàâëÿåò äâà êëàññà: Symfony\Component\HttpFoundation\Request
    è Symfony\Component\HttpFoundation\Response. Ýòè êëàññû ÿâëÿþòñÿ îáúåêòíîîðèåíòèðîâàííûì ïðåäñòàâëåíèåì íåîáðàáîòàííîãî HTTP-çàïðîñà, êîòîðûé ïîäëåæèò
    îáðàáîòêå è ñîîòâåòñòâóþùåãî åìó HTTP-îòâåòà, êîòîðûé áóäåò âîçâðàùåí êëèåíòó.
    Èñïîëüçóéòå èõ äëÿ óëó÷øåíèÿ áëîãà:
    // index.php
    require_once 'app/bootstrap.php';
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    $request = Request::createFromGlobals();
    $uri = $request->getPathInfo();
    if ($uri == '/') {
    $response = list_action();
    } elseif ($uri == '/show' && $request->query->has('id')) {
    $response = show_action($request->query->get('id'));
    } else {
    $html = '

    Page Not Found

    ';
    $response = new Response($html, 404);
    }
    // Âûâîä çàãîëîâêîâ è îòïðàâêà îòâåòà
    $response->send();

    Êîíòðîëëåðû òåïåðü îòâå÷àþò çà âîçâðàò îáúåêòà Response. Äëÿ òîãî ÷òîáû óïðîñòèòü
    ïðîöåññ ñîçäàíèÿ îòâåòà, âû ìîæåòå äîáàâèòü íîâóþ ôóíêöèþ render_template(), êîòîðàÿ, ìåæäó ïðî÷èì, äåéñòâóåò ïðàêòè÷åñêè êàê äâèæîê øàáëîíîâ Symfony2:
    // controllers.php

    2.2.

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    31

    Symfony Documentation, Âûïóñê 2.0

    use Symfony\Component\HttpFoundation\Response;
    function list_action()
    {
    $posts = get_all_posts();
    $html = render_template('templates/list.php', array('posts' => $posts));
    }

    return new Response($html);

    function show_action($id)
    {
    $post = get_post_by_id($id);
    $html = render_template('templates/show.php', array('post' => $post));
    }

    return new Response($html);

    // Ôóíêöèÿ-ïîìîùíèê äëÿ îòîáðàæåíèÿ øàáëîíîâ
    function render_template($path, array $args)
    {
    extract($args);
    ob_start();
    require $path;
    $html = ob_get_clean();
    }

    return $html;

    Ïîëó÷èâ â ïîìîùü íåáîëüøóþ ÷àñòü Symfony2, ïðèëîæåíèå ñòàëî áîëåå ãèáêèì è íàä¼æíûì. Request ïðåäîñòàâëÿåò íàä¼æíûé ñïîñîá ïîëó÷èòü èíôîðìàöèþ î çàïðîñå. Ê
    ïðèìåðó, ìåòîä getPathInfo() âîçâðàùàåò î÷èùåííûé URI (âñåãäà âîçâðàùàåò /show
    è íèêîãäà /index.php/show). Òàêèì îáðàçîì, äàæå åñëè ïîëüçîâàòåëü îòêðîåò â áðàóçåðå /index.php/show, ïðèëîæåíèå âûïîëíèò show_action().
    Îáúåêò Response ïðåäîñòàâëÿåò ãèáêîñòü â ïîñòðîåíèè HTTP-îòâåòà, ïîçâîëÿÿ äîáàâëÿòü HTTP çàãîëîâêè è êîíòåíò ñòðàíèöû ïîñðåäñòâîì îáúåêòíî-îðèåíòèðîâàííîãî
    èíòåðôåéñà. È, õîòÿ â ýòîì ïðèëîæåíèè ïîêà ÷òî îòâåòû âåñüìà ïðîñòû, ýòà ãèáêîñòü
    âûïëàòèò âàì äèâèäåíäû ïî ìåðå ðîñòà ïðèëîæåíèÿ.
    Ïðîñòîå ïðèëîæåíèå íà Symfony2

    Áëîã íà÷àë ñâîé äëèííûé ïóòü, íî îí âñ¼ åù¼ ñîäåðæèò ñëèøêîì ìíîãî êîäà äëÿ òàêîãî
    íåáîëüøîãî ïðèëîæåíèÿ. Ñëåäóÿ ïî ïóòè, ìû èçîáðåëè ïðîñòóþ ñèñòåìó ìàðøðóòèçàöèè è ìåòîä, èñïîëüçóþùèé ob_start() è ob_get_clean() äëÿ îòîáðàæåíèÿ øàáëîíîâ.
    Åñëè, ïî êàêèì-ëèáî ñîîáðàæåíèÿì, âû õîòèòå ïðîäîëæèòü ñîçäàíèå ýòîãî ôðåéìâîð32

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    êà ñ íóëÿ, âû ìîæåòå ïî êðàéíåé ìåðå èñïîëüçîâàòü ñàìîñòîÿòåëüíûå êîìïîíåíòû
    Symfony - Routing è Templating, êîòîðûå ðåøàþò ýòè ïðîáëåìû.
    Âìåñòî òîãî ÷òîáû çàíîâî ðåøàòü òèïîâûå ïðîáëåìû, âû ìîæåòå ïðåäîñòàâèòü Symfony2
    çàáîòó î íèõ. Âîò ïðèìåð ïðîñòîãî ïðèëîæåíèÿ, ïîñòðîåííîãî ñ èñïîëüçîâàíèåì
    Symfony2:
    // src/Acme/BlogBundle/Controller/BlogController.php
    namespace Acme\BlogBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    class BlogController extends Controller
    {
    public function listAction()
    {
    $posts = $this->get('doctrine')->getEntityManager()
    ->createQuery('SELECT p FROM AcmeBlogBundle:Post p')
    ->execute();
    }

    return $this->render('AcmeBlogBundle:Post:list.html.php', array('posts' => $posts));

    public function showAction($id)
    {
    $post = $this->get('doctrine')
    ->getEntityManager()
    ->getRepository('AcmeBlogBundle:Post')
    ->find($id);
    if (!$post) {
    // cause the 404 page not found to be displayed
    throw $this->createNotFoundException();
    }

    }

    }

    return $this->render('AcmeBlogBundle:Post:show.html.php', array('post' => $post));

    Ýòè äâà êîíòðîëëåðà âñ¼ åù¼ ëåãêîâåñíû. Êàæäûé èç íèõ èñïîëüçóåò áèáëèîòåêó
    Doctrine ORM äëÿ ïîëó÷åíèÿ îáúåêòîâ èç áàçû äàííûõ è êîìïîíåíò Templating äëÿ
    îòîáðàæåíèÿ øàáëîíà è âîçâðàòà îáúåêòà Response. Øàáëîí list òåïåðü ñòàë åù¼ íåìíîãî ïðîùå:

    extend('::layout.html.php') ?>

    2.2.

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    33

    Symfony Documentation, Âûïóñê 2.0

    set('title', 'List of Posts') ?>

    List of Posts





    • {% block title %} List of Posts{% endblock %}
      {% block body %}

      List of Posts



      {% endblock %}

      Ñîîòâåòñòâóþùèé øàáëîí layout.html.twig åù¼ ïðîùå:
      {# app/Resources/views/layout.html.twig #}


      {% block title %} Default title{% endblock %}


      {% block body %} {% endblock %}



      Twig îòëè÷íî èíòåãðèðîâàí ñ Symfony2.  òî âðåìÿ, êàê PHP øàáëîíû áóäóò âñåãäà
      ïîääåðæèâàòüñÿ â Symfony2, ìû òàêæå áóäåì ïðîäîëæàòü îáñóæäåíèÿ ïðåèìóùåñòâ
      Twig. Áîëüøå èíôîðìàöèè î Twig âû íàéäåòå â ãëàâå î øàáëîíàõ .

      36

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      2.2.5 Äîïîëíèòåëüíàÿ èíôîðìàöèÿ â Cookbook

      ˆ

      Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig

      ˆ

      Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ

      2.3 Óñòàíîâêà è íàñòðîéêà Symfony2
      Öåëü ýòîé ãëàâû ïîìî÷ü âàì íàñòðîèòü è çàïóñòèòü ðàáî÷åå ïðèëîæåíèå, ñîçäàííîå ïðè
      ïîìîùè Symfony. Ê ñ÷àñòüþ, Symfony ïðåäëàãàåò äèñòðèáóòèâû, êîòîðûå ïðåäñòàâëÿþò ñîáîé áàçîâûå ïðîåêòû, êîòîðûå âû ìîæåòå çàãðóçèòü è íåçàìåäëèòåëüíî íà÷àòü
      ðàçðàáîòêó.

      Ñîâåò: Åñëè âû èùèòå ðóêîâîäñòâî ïî ñîçäàíèþ íîâîãî ïðîåêòà è ðàçìåùåíèþ åãî â
      ñèñòåìå êîíòðîëÿ âåðñèé, ïåðåéäèòå ê ñåêöèè Èñïîëüçîâàíèå ñèñòåìû êîíòðîëÿ âåðñèé.

      2.3.1 Çàãðóçêà äèñòðèáóòèâà Symfony2

      Ñîâåò: Ïðåæäå âñåãî, óäîñòîâåðüòåñü, ÷òî ó âàñ óñòàíîâëåí è íàñòðîåí Web-ñåðâåð

      (íàïðèìåð, Apache) è èíòåðïðåòàòîð PHP 5.3.2 èëè áîëåå íîâûé. Áîëåå ïîäðîáíóþ èíôîðìàöèþ î ñèñòåìíûõ òðåáîâàíèÿõ Symfony2 â ìîæåòå íàéòè â ðàçäåëå Ñèñòåìíûå
      òðåáîâàíèÿ.
      Äèñòðèáóòèâû Symfony2 ïðåäñòàâëÿþò ñîáîé ïîëíîôóíêöèîíàëüíûå ïðèëîæåíèÿ,
      âêëþ÷àþùèå ÿäðî Symfony2, íàáîð ïîëåçíûõ ïàêåòîâ (Bundles), ðàçóìíóþ ñòðóêòóðó äèðåêòîðèé è êîíôèãóðàöèþ ïî óìîë÷àíèþ. Êîãäà âû çàãðóæàåòå äèñòðèáóòèâ
      Symfony2, âû ôàêòè÷åñêè çàãðóæàåòå ñêåëåòîí ôóíêöèîíèðóþùåãî ïðèëîæåíèÿ, êîòîðûé òóò æå ìîæíî íà÷àòü èñïîëüçîâàòü êàê áàçó äëÿ âàøåãî ñîáñòâåííîãî ïðèëîæåíèÿ.
      Íà÷í¼ì ñî ñòðàíèöû çàãðóçêè Symfony2 http://symfony.com/download. Íà ýòîé ñòðàíèöå
      âû ìîæåòå âèäåòü äèñòðèáóòèâ Symfony Standard Edition, êîòîðûé ÿâëÿåòñÿ îñíîâíûì
      äèñòðèáóòèâîì. Òåïåðü âàì íóæíî ïðèíÿòü 2 ðåøåíèÿ:
      ˆ Çàãðóçèòü ëèáî .tgz ëèáî .zip àðõèâ - îíè èäåíòè÷íû, ïðîñòî âîïðîñ ïðåäïî÷òåíèé.
      ˆ Çàãðóçèòü äèñòðèáóòèâ, âêëþ÷àþùèé ñòîðîííèå áèáëèîòåêè èëè æå íå âêëþ÷àþùèé (with/without vendors). Åñëè ó âàñ óñòàíîâëåí Git, âû ìîæåòå çàãðóçèòü
      Symfony2 without vendors, òàê òàê ýòî äàñò âàì íåìíîãî áîëüøå âîçìîæíîñòåé
      ïî âêëþ÷åíèþ ñòîðîííèõ áèáëèîòåê/âåíäîðîâ.

      2.3.

      Óñòàíîâêà è íàñòðîéêà Symfony2

      37

      Symfony Documentation, Âûïóñê 2.0

      Çàãðóçèòå îäèí èç àðõèâîâ â root-äèðåêòîðèþ âàøåãî ëîêàëüíîãî web-ñåðâåðà è ðàñïàêóéòå åãî. Â êîìàíäíîé ñòðîêå UNIX ýòî ìîæíî âûïîëíèòü ïðè ïîìîùè îäíîé èç ýòèõ
      êîìàíä (çàìåíÿÿ ### àêòóàëüíûì èìåíåì ôàéëà):
      # for .tgz file
      tar zxvf Symfony_Standard_Vendors_2.0.###.tgz
      # for a .zip file
      unzip Symfony_Standard_Vendors_2.0.###.zip

      Êîãäà âû âûïîëíèòå ýòó îïåðàöèþ, ó âàñ áóäåò äèðåêòîðèÿ Symfony/, êîòîðàÿ áóäåò
      âûãëÿäåòü ïðèìåðíî òàê:
      www/ <- root äèðåêòîðèÿ âàøåãî âåá-ñåðâåðà
      Symfony/ <- ðàñïàêîâàííûé àðõèâ
      app/
      cache/
      config/
      logs/
      src/
      ...
      vendor/
      ...
      web/
      app.php
      ...

      Îáíîâëåíèå Âåíäîðîâ

      Äàëåå, åñëè âû çàãðóçèëè àðõèâ áåç âåíäîðîâ (without vendors), íåîáõîäèìî èõ óñòàíîâèòü, âûïîëíèâ ñëåäóþùóþ êîìàíäó:
      php bin/vendors install

      Ýòà êîìàíäà çàãðóçèò âñå íåîáõîäèìûå áèáëèîòåêè, âêëþ÷àÿ ñîáñòâåííî Symfony, â
      äèðåêòîðèþ vendor/. Áîëåå ïîäðîáíóþ èíôîðìàöèþ î òîì, êàê óïðàâëÿòü ñòîðîííèìè áèáëèîòåêàìè â Symfony2 âû ìîæåòå ïîëó÷èòü â ðàçäåëå  Óïðàâëåíèå âíåøíèìè
      áèáëèîòåêàìè ñ ïîìîùüþ bin/vendors è deps .
      Êîíôèãóðàöèÿ è íàñòðîéêà

      Íà òåêóùèé ìîìåíò âñå íåîáõîäèìûå ñòîðîííèå áèáëèîòåêè òåïåðü ðàñïîëàãàþòñÿ â
      äèðåêòîðèè vendor/. Òàêæå â äèðåêòîðèè app/ ðàñïîëîæåíû íàñòðîéêè ïî-óìîë÷àíèþ,
      à â äèðåêòîðèè src/ ïðèìåð êîäà.

      38

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Symfony2 ïîñòàâëÿåòñÿ ñ âèçóàëüíûì òåñòåðîì êîíôèãóðàöèè âåá-ñåðâåðà, äëÿ òîãî
      ÷òîáû ïîìî÷ü âàì îïðåäåëèòü, ïîäõîäèò ëè êîíôèãóðàöèÿ âàøåãî ñåðâåðà è PHP äëÿ
      Symfony. Èñïîëüçóéòå ñëåäóþùèé URL äëÿ ïðîâåðêè êîíôèãóðàöèè:
      http://localhost/Symfony/web/config.php

      Åñëè ïðîâåðêà ïîêàçûâàåò êàêèå-ëèáî íåñîîòâåòñòâèÿ - èñïðàâüòå èõ, ïðåæäå ÷åì äâèãàòüñÿ äàëåå.

      2.3.

      Óñòàíîâêà è íàñòðîéêà Symfony2

      39

      Symfony Documentation, Âûïóñê 2.0

      Íàñòðîéêà ïðàâ äîñòóïà
      Îäíî èç òèïîâûõ çàìå÷àíèé çàêëþ÷àåòñÿ â òîì, ÷òî äèðåêòîðèè app/cache è
      app/logs äîëæíû èìåòü ïðàâà íà çàïèñü êàê äëÿ âåá-ñåðâåðà, òàê è äëÿ ïîëüçîâàòåëÿ, îò èìåíè êîòîðîãî âûïîëíÿþòñÿ êîìàíäû èç êîìàíäíîé ñòðîêè.  UNIXñèñòåìàõ, åñëè ïîëüçîâàòåëü, èç-ïîä êîòîðîãî çàïóñêàåòñÿ âåá-ñåðâåð îòëè÷àåòñÿ
      îò ïîëüçîâàòåëÿ êîìàíäíîé ñòðîêè, âû ìîæåòå âûïîëíèòü ñëåäóþùèå êîìàíäû,
      äëÿ òîãî ÷òîáû áûòü óâåðåííûìè, ÷òî ïðàâà äîñòóïà íàñòðîåíû âåðíî. Çàìåíÿéòå
      www-data íà ïîëüçîâàòåëÿ âåá-ñåðâåðà è yourname íà âàøåãî ïîëüçîâàòåëÿ êîìàíäíîé ñòðîêè:

      1. Èñïîëüçîâàíèå ACL â ñèñòåìàõ, êîòîðûå ïîääåðæèâàþò chmod +a

      Ìíîãèå ñèñòåìû ïîçâîëÿþò èñïîëüçîâàòü êîìàíäó chmod +a. Ïîïðîáóéòå âûïîëíèòü å¼, è åñëè âû ïîëó÷èòå ñîîáùåíèå îá îøèáêå - ïðîáóéòå ñëåäóþùèé ìåòîä:
      rm -rf app/cache/*
      rm -rf app/logs/*

      sudo chmod +a "www-data allow delete,write,append,file_inherit,directory_inherit" app/cache app
      sudo chmod +a "yourname allow delete,write,append,file_inherit,directory_inherit" app/cache app

      2. Èñïîëüçîâàíèå Acl íà ñèñòåìàõ, êîòîðûå íå ïîääåðæèâàþò chmod +a

      Íåêîòîðûå ñèñòåìû íå ïîääåðæèâàþò chmod +a, íî ïîääåðæèâàþò äðóãóþ óòèëèòó,
      setfacl. Âîçìîæíî, âàì ïîòðåáóåòñÿ âêëþ÷èòü ïîääåðæêó ACL íà âàøåì ðàçäåëå è óñòàíîâèòü setfacl ïåðåä òåì êàê èñïîëüçîâàòü (ýòî ìîæåò ïîòðåáîâàòüñÿ,
      íàïðèìåð, åñëè âû èñïîëüçóåòå Ubuntu):
      sudo setfacl -R -m u:www-data:rwx -m u:yourname:rwx app/cache app/logs
      sudo setfacl -dR -m u:www-data:rwx -m u:yourname:rwx app/cache app/logs

      3. Áåç èñïîëüçîâàíèÿ ACL

      Åñëè ó âàñ íåò ïðàâ íà èçìåíåíèå ACL äëÿ äèðåêòîðèé, âàì ïîòðåáóåòñÿ èçìåíèòü
      umask òàêèì îáðàçîì, ÷òîáû äèðåêòîðèè cache è log áûëè äîñòóïíû íà çàïèñü
      ãðóïïå èëè æå âñåì (world-writable) â çàâèñèìîñòè îò òîãî íàõîäÿòñÿ ëè ïîëüçîâàòåëè âåá-ñåðâåðà è êîìàíäíîé ñòðîêè â îäíîé ãðóïïå èëè íåò. Äëÿ ýòîãî íóæíî âñòàâèòü ñëåäóþùóþ ñòðî÷êó â íà÷àëî ôàéëîâ app/console, web/app.php è
      web/app_dev.php:
      umask(0002); // Ðàçðåøàåò èñïîëüçîâàòü ïðàâà 0775
      // èëè
      umask(0000); // Ðàçðåøàåò èñïîëüçîâàòü ïðàâà 0777

      Èìåéòå â âèäó, ÷òî èñïîëüçîâàíèå ACL ïðåäïî÷òèòåëüíåå, êîãäà âû èìååòå äîñòóï
      ê íèì íà ñåðâåðå, ïîòîìó ÷òî ñìåíà umask íå ÿâëÿåòñÿ thread-safe.
      Êîãäà âñå íåîáõîäèìûå ïðèãîòîâëåíèÿ âûïîëíåíû, êëèêíèòå íà ññûëêó Go to the
      Welcome page è ïåðåéäèòå íà âàøó ïåðâóþ íàñòîÿùóþ ñòðàíèöó Symfony2:

      40

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      http://localhost/Symfony/web/app_dev.php/

      Symfony2 ïîçäîðîâêàåòñÿ è ïîçäðàâèò âàñ ñ ïðîäåëàííîé òÿæåëîé ðàáîòîé!!

      2.3.2 Íà÷àëî ðàçðàáîòêè

      Òåïåðü, êîãäà ìû èìååì íàñòðîåííîå Symfony2 ïðèëîæåíèå, âû ìîæåòå íà÷àòü ðàçðàáîòêó. Âàø äèñòðèáóòèâ ìîæåò ñîäåðæàòü ïðèìåðû êîäà - ïðî÷òèòå ôàéë README.rst
      èç äèñòðèáóòèâà (ýòî îáû÷íûé òåêñòîâûé ôàéë) äëÿ òîãî ÷òîáû îçíàêîìèòüñÿ ñ òåì,
      êàêèå ïðèìåðû âêëþ÷åíû â äàííûé äèñòðèáóòèâ è êàê èõ ìîæíî áóäåò óäàëèòü ïîçäíåå.
      Åñëè âû íîâè÷îê â Symfony, îçíàêîìüòåñü ñ ðóêîâîäñòâîì  Ñîçäàíèå ñòðàíèö â
      Symfony2 , ãäå âû óçíàåòå, êàê ñîçäàâàòü ñòðàíèöû, èçìåíÿòü íàñòðîéêè è âîîáùå äåëàòü âñ¼ íåîáõîäèìîå äëÿ ñîçäàíèÿ íîâîãî ïðèëîæåíèÿ.
      2.3.3 Èñïîëüçîâàíèå ñèñòåìû êîíòðîëÿ âåðñèé

      Åñëè âû èñïîëüçóåòå ñèñòåìó êîíòðîëÿ âåðñèé òèïà Git èëè Subversion, âû ìîæåòå
      íàñòðîèòü âàøó ñèñòåìó è íà÷àòü êîììèòèòü âàø ïðîåêò êàê âû ýòî äåëàåòå îáû÷íî.
      Symfony Standard - ýòî òî÷êà îòñ÷¼òà äëÿ âàøåãî íîâîãî ïðîåêòà.
      Áîëåå ïîäðîáíûå èíñòðóêöèè î òîì, êàê ëó÷øå âñåãî íàñòðîèòü ïðîåêò äëÿ õðàíåíèÿ â
      git, çàãëÿíèòå ñþäà: Êàê ñîçäàòü è ðàçìåñòèòü Ïðîåêò íà Symfony2 â git-ðåïîçèòîðèè .

      2.3.

      Óñòàíîâêà è íàñòðîéêà Symfony2

      41

      Symfony Documentation, Âûïóñê 2.0

      Èãíîðèðóåì äèðåêòîðèþ

      vendor/

      Åñëè âû çàãðóçèëè àðõèâ áåç âåíäîðîâ âû ìîæåòå ñïîêîéíî èãíîðèòü äèðåêòîðèþ
      vendor/ öåëèêîì è íå êîììèòèòü å¼ ñîäåðæèìîå â ñèñòåìó êîíòðîëÿ âåðñèé. Â Git
      ýòîãî ìîæíî äîáèòüñÿ, ñîçäàâ ôàéë .gitignore è äîáàâèâ â íåãî ñëåäóþùóþ ñòðîêó:
      vendor/

      Ïîñëå ýòîãî äèðåêòîðèÿ vendor íå áóäåò ó÷àñòâîâàòü â êîììèòàõ. Ýòî çäîðîâî (ïðàâäàïðàâäà!), ïîòîìó ÷òî êîãäà êòî-òî åùå êëîíèðóåò èëè âûãðóçèò âàø ïðîåêò îí ñìîæåò
      çàïðîñòî âûïîëíèòü ñêðèïò php bin/vendors install è çàãðóçèòü âñå íåîáõîäèìûå
      áèáëèîòåêè.

      2.4 Ñîçäàíèå ñòðàíèö â Symfony2
      Ñîçäàíèå íîâîé ñòðàíèöû â Symfony2 ýòî ïðîñòîé ïðîöåññ, ñîñòîÿùèé èç 2 øàãîâ:
      ˆ

      Ñîçäàíèå ìàðøðóòà : Ìàðøðóò îïðåäåëÿåò URL (íàïðèìåð /about) äëÿ âàøåé
      ñòðàíèöû, à òàêæå êîíòðîëëåð (PHP ôóíêöèÿ), êîòîðûé Symfony2 äîëæåí âûïîëíèòü, êîãäà URL âõîäÿùåãî çàïðîñà ñîâïàäåò øàáëîíîì ìàðøðóòà;

      ˆ

      Ñîçäàíèå êîíòðîëëåðà :

      Êîíòðîëëåð  ýòî PHP ôóíêöèÿ, êîòîðàÿ ïðèíèìàåò âõîäÿùèé çàïðîñ è ïðåîáðàçóåò åãî â îáúåêò Response, êîòîðûé áóäåò âîçâðàùåí
      ïîëüçîâàòåëþ.

      Íàì íðàâèòñÿ òàêîé ïîäõîä, ïîòîìó ÷òî îí ñîîòâåòñòâóåò òîìó, êàê ðàáîòàåò Web. Êàæäîå âçàèìîäåéñòâèå â Web èíèöèàëèçèðóåòñÿ HTTP çàïðîñîì. Çàáîòà âàøåãî ïðèëîæåíèÿ  èíòåðïðåòèðîâàòü çàïðîñ è âåðíóòü ñîîòâåòñòâóþùèé îòâåò.
      Symfony2 ñëåäóåò ýòîé ôèëîñîôèè è ïðåäîñòàâëÿåò âàì èíñòðóìåíòû è ñîãëàøåíèÿ,
      äëÿ òîãî ÷òîáû âàøå ïðèëîæåíèå îñòàâàëîñü õîðîøî ñòðóêòóðèðîâàííûì ïðè ðîñòå åãî
      ïîñåùàåìîñòè è ñëîæíîñòè.
      Çâó÷èò ïðîñòî? Äàâàéòå êîïí¼ì ïî ãëóáæå!
      2.4.1 Ñòðàíèöà Hello Symfony!

      Äàâàéòå íà÷íåì ñ êëàññè÷åñêîãî ïðèëîæåíèÿ Hello World!. Êîãäà âû çàêîí÷èòå ðàáîòó
      íàä íèì, ïîëüçîâàòåëü ïðèëîæåíèÿ áóäåò èìåòü âîçìîæíîñòü ïîëó÷èòü ïåðñîíàëüíîå
      ïðèâåòñòâèå, (íàïðèìåð Hello Symfony), ïåðåéäÿ ïî ñëåäóþùåìó URL:
      http://localhost/app_dev.php/hello/Symfony

      Âû òàêæå ñìîæåòå çàìåíèòü Symfony íà äðóãîå èìÿ è ïîëó÷èòü íîâîå ïðèâåòñòâèå. Äëÿ
      ñîçäàíèÿ ýòîé ñòðàíèöû ìû ïðîéäåì ïðîñòîé ïóòü èç äâóõ øàãîâ.

      42

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Ïðèìå÷àíèå: Äàííîå ðóêîâîäñòâî ïîäðàçóìåâàåò, ÷òî âû óæå ñêà÷àëè Symfony2 è íàñòðîèëè âàø âåá-ñåðâåð. URL, óêàçàííûé âûøå, ïîäðàçóìåâàåò, ÷òî localhost óêàçûâàåò
      íà web-äèðåêòîðèþ âàøåãî íîâîãî Symfony2 ïðîåêòà. Åñëè æå âû åù¼ íå âûïîëíèëè
      ýòèõ øàãîâ, ðåêîìåíäóåòñÿ èõ âûïîëíèòü, ïðåæäå ÷åì âû ïðîäîëæèòå ÷òåíèå. Äîïîëíèòåëüíóþ èíôîðìàöèþ âû ìîæåòå íàéòè â ãëàâå Óñòàíîâêà è íàñòðîéêà Symfony2 .

      Ïðåæäå ÷åì íà÷àòü: ñîçäàíèå Ïàêåòà (bundle)

      Ïðåæäå ÷åì íà÷àòü, âàì íåîáõîäèìî ñîçäàòü ïàêåò (bundle ).  Symfony2 ïàêåò íàïîìèíàåò plugin, çà èñêëþ÷åíèåì òîãî, ÷òî âåñü êîä âàøåãî ïðèëîæåíèÿ áóäåò ðàñïîëîæåí
      âíóòðè òàêîãî ïàêåòà.
      Âîîáùå ãîâîðÿ, ïàêåò  ýòî íå áîëåå ÷åì äèðåêòîðèÿ, êîòîðàÿ ñîäåðæèò âñå ÷òî îòíîñèòñÿ ê êàêîé-òî ñïåöèôè÷åñêîé ôóíêöèè, âêëþ÷àÿ PHP-êëàññû, íàñòðîéêè è äàæå
      ñòèëè è ôàéëû Javascript (ñì. Ñèñòåìà ïàêåòîâ ).
      Äëÿ ñîçäàíèÿ ïàêåòà ñ èìåíåì AcmeHelloBundle (äåìî-ïàêåò, êîòîðûé âû ñîçäàäèòå â
      õîäå ïðî÷òåíèÿ äàííîé ñòàòüè), íåîáõîäèìî âûïîëíèòü ñëåäóþùóþ êîìàíäó è ñëåäîâàòü èíñòðóêöèÿì, êîòîðûå ïîÿâÿòñÿ íà ýêðàíå (óñòàíîâèòå âñå îïöèè ïî-óìîë÷àíèþ):
      php app/console generate:bundle --namespace=Acme/HelloBundle --format=yml

      Çà êóëèñàìè æå ïðîèçîéä¼ò âîò ÷òî: áóäåò ñîçäàíà äèðåêòîðèÿ äëÿ ïàêåòà
      src/Acme/HelloBundle. Òàêæå â ôàéë app/AppKernel.php àâòîìàòè÷åñêè áóäåò äîáàâëåíà ñòðîêà, êîòîðàÿ çàðåãèñòðèðóåò âíîâü ñîçäàííûé ïàêåò:
      // app/AppKernel.php
      public function registerBundles()
      {
      $bundles = array(
      // ...
      new Acme\HelloBundle\AcmeHelloBundle(),
      );
      // ...
      }

      return $bundles;

      Òåïåðü, êîãäà âû ñîçäàëè è èíèöèàëèçèðîâàëè ïàêåò, âû ìîæåòå ïðèñòóïèòü ê ñîçäàíèþ
      âàøåãî ïðèëîæåíèÿ.

      2.4.

      Ñîçäàíèå ñòðàíèö â Symfony2

      43

      Symfony Documentation, Âûïóñê 2.0

      Øàã 1: Ñîçäàíèå ìàðøðóòà

      Ïî óìîë÷àíèþ, êîíôèãóðàöèîííûé ôàéë ìàðøðóòèçàòîðà â ïðèëîæåíèè Symfony2,
      ðàñïîëàãàåòñÿ â app/config/routing.yml. Äëÿ êîíôèãóðèðîâàíèÿ ìàðøðóòèçàòîðà, à
      òàêæå ëþáûõ ïðî÷èõ êîíôèãóðàöèé Symfony2, âû ìîæåòå òàêæå èñïîëüçîâàòü XML
      èëè PHP ôîðìàò.
      Åñëè âû ïîñìîòðèòå â îñíîâíîé êîíôèãóðàöèîííûé ôàéë, âû óâèäèòå, ÷òî Symfony óæå
      äîáàâèë çàïèñü äëÿ ñãåíåðèðîâàííîãî AcmeHelloBundle:
      ˆ

      YAML

      # app/config/routing.yml
      AcmeHelloBundle:
      resource: "@AcmeHelloBundle/Resources/config/routing.yml"
      prefix: /

      ˆ

      XML




      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing



      ˆ

      PHP

      // app/config/routing.php
      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->addCollection(
      $loader->import('@AcmeHelloBundle/Resources/config/routing.php'),
      '/',
      );
      return $collection;

      Ýòà çàïèñü î÷åíü ïðîñòà: îíà ñîîáùàåò Symfony, ÷òî íåîáõîäèìî çàãðóçèòü êîíôèãóðàöèþ ìàðøðóòèçàòîðà èç ôàéëà Resources/config/routing.yml, êîòîðûé ðàñïîëîæåí â ïàêåòå AcmeHelloBundle. Ýòî îçíà÷àåò, ÷òî âû ìîæåòå ðàçìåøàòü êîíôèãóðàöèþ

      44

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ìàðøðóòèçàòîðà íåïîñðåäñòâåííî â app/config/routing.yml èëè æå õðàíèòü ìàðøðóòû âíóòðè ïàêåòà è èìïîðòèðîâàòü èõ îòòóäà.
      Òåïåðü, êîãäà ôàéë routing.yml èìïîðòèðîâàí èç ïàêåòà, äîáàâüòå íîâûé ìàðøðóò,
      êîòîðûé îïðåäåëèò URL ñòðàíèöû, êîòîðóþ âû ñîáèðàåòåñü ñîçäàòü:
      ˆ

      YAML

      # src/Acme/HelloBundle/Resources/config/routing.yml
      hello:
      pattern: /hello/{name}
      defaults: { _controller: AcmeHelloBundle:Hello:index }

      ˆ

      XML




      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeHelloBundle:Hello:index



      ˆ

      PHP

      // src/Acme/HelloBundle/Resources/config/routing.php
      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('hello', new Route('/hello/{name}', array(
      '_controller' => 'AcmeHelloBundle:Hello:index',
      )));
      return $collection;

      Ìàðøðóò ñîñòîèò èç äâóõ îñíîâíûõ ÷àñòåé: øàáëîíà (pattern), ñ êîòîðûì ñðàâíèâàåòñÿ URL, à òàêæå ìàññèâà ïàðàìåòðîâ ïî óìîë÷àíèþ (defaults), â êîòîðîì óêàçûâàåòñÿ
      êîíòðîëëåð, êîòîðûé íåîáõîäèìî âûïîëíèòü. Çàïîëíèòåëü {name} â øàáëîíå  ýòî ìåòàñèìâîë (wildcard). Îí îçíà÷àåò, ÷òî URL /hello/Ryan, /hello/Fabien, à òàêæå ïðî÷èå,
      ïîõîæèå íà íèõ, áóäóò ñîîòâåòñòâîâàòü ýòîìó æå ìàðøðóòó. Ïàðàìåòð, îïðåäåë¼ííûé
      çàïîëíèòåëåì {name}, òàêæå áóäåò ïåðåäàí â êîíòðîëëåð, òàê ÷òî âû ñìîæåòå èñïîëüçîâàòü åãî, ÷òîáû ïîïðèâåòñòâîâàòü ïîëüçîâàòåëÿ.
      2.4.

      Ñîçäàíèå ñòðàíèö â Symfony2

      45

      Symfony Documentation, Âûïóñê 2.0

      Ïðèìå÷àíèå: Ñèñòåìà ìàðøðóòèçàöèè èìååò åùå ìíîæåñòâî çàìå÷àòåëüíûõ ôóíêöèé
      äëÿ ñîçäàíèÿ ãèáêèõ è ôóíêöèîíàëüíûõ ñòðóêòóð URL â ïðèëîæåíèè. Çà äîïîëíèòåëüíîé èíôîðìàöèåé âû ìîæåòå îáðàòèòüñÿ ê ãëàâå Ìàðøðóòèçàöèÿ .

      Øàã 2: Ñîçäàíèå Êîíòðîëëåðà

      Êîãäà URI âèäà /hello/Ryan îáíàðóæèâàåòñÿ ïðèëîæåíèåì â çàïðîñå, ìàðøðóò hello
      ñîâïàä¼ò ñ íèì è áóäåò âûçâàí êîíòðîëëåð AcmeHelloBundle:Hello:index. Ñëåäóþùèì
      âàøèì øàãîì áóäåò ñîçäàíèå ýòîãî êîíòðîëëåðà.
      Êîíòðîëëåð AcmeHelloBundle:Hello:index - ýòî ëîãè÷åñêîå èìÿ êîíòðîëëåðà è îíî ñîîòâåòñòâóåò ìåòîäó indexAction PHP-êëàññà, èìåíóåìîãî
      Acme\HelloBundle\Controller\Hello. Ïðèñòóïèì ê ñîçäàíèþ ýòîãî ôàéëà âíóòðè AcmeHelloBundle:
      // src/Acme/HelloBundle/Controller/HelloController.php
      namespace Acme\HelloBundle\Controller;
      use Symfony\Component\HttpFoundation\Response;
      class HelloController
      {
      }

      Â äåéñòâèòåëüíîñòè, êîíòðîëëåð  ýòî íå ÷òî èíîå, êàê ìåòîä PHP êëàññà, êîòîðûé
      âû ñîçäà¼òå, à Symfony âûïîëíÿåò. Ýòî òî ìåñòî, ãäå âàø êîä, èñïîëüçóÿ èíôîðìàöèþ
      èç çàïðîñà, ñîçäàåò çàïðîøåííûé ðåñóðñ. Çà èñêëþ÷åíèåì íåêîòîðûõ îñîáûõ ñëó÷àåâ,
      ðåçóëüòàòîì ðàáîòû êîíòðîëëåðà âñåãäà ÿâëÿåòñÿ îáúåêò Symfony2 Response.
      Ñîçäàéòå ìåòîä indexAction, êîòîðûé Symfony âûïîëíèò, êîãäà ñðàáîòàåò ìàðøðóò
      hello:
      // src/Acme/HelloBundle/Controller/HelloController.php
      // ...
      class HelloController
      {
      public function indexAction($name)
      {
      return new Response('Hello '.$name.'!');

      46

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      }

      }

      Ýòîò êîíòðîëëåð ïðåäåëüíî ïðîñò: îí ñîçäàåò íîâûé îáúåêò Response, ÷üèì ïåðâûì
      àðãóìåíòîì ÿâëÿåòñÿ êîíòåíò, êîòîðûé áóäåò èñïîëüçîâàí äëÿ ñîçäàíèÿ îòâåòà (â íàøåì
      ñëó÷àå ýòî ìàëåíüêàÿ HTML-ñòðàíèöà, êîä êîòîðîé ìû óêàçàëè ïðÿìî â êîíòðîëëåðå).
      Ïðèìèòå íàøè ïîçäðàâëåíèÿ! Ïîñëå ñîçäàíèÿ âñåãî ëèøü ìàðøðóòà è êîíòðîëëåðà, âû
      óæå èìååòå ïîëíîöåííóþ ñòðàíèöó! Åñëè âû âñå íàñòðîèëè êîððåêòíî, âàøå ïðèëîæåíèå
      äîëæíî ïîïðèâåòñòâîâàòü âàñ:
      http://localhost/app_dev.php/hello/Ryan

      Ñîâåò: Âû òàêæå ìîæåòå îòîáðàçèòü âàøå ïðèëîæåíèå â
      æåíèè ,

      ïðîäóêòîâîì (prod) îêðó-

      ïîñåòèâ ñëåäóþùèé URL:

      http://localhost/app.php/hello/Ryan

      Åñëè âû óâèäèòå îøèáêó, òî ñêîðåå âñåãî âàì âñåãî ëèøü íåîáõîäèìî î÷èñòèòü êýø,
      âûïîëíèâ êîìàíäó:
      php app/console cache:clear --env=prod --no-debug

      Íå îáÿçàòåëüíûì (íî, êàê ïðàâèëî, âîñòðåáîâàííûì) òðåòüèì øàãîì ÿâëÿåòñÿ ñîçäàíèå
      øàáëîíà.

      Ïðèìå÷àíèå: Êîíòðîëëåð  ýòî ãëàâíàÿ òî÷êà âõîäà äëÿ âàøåãî êîäà è êëþ÷åâîé
      èíãðåäèåíò ïðè ñîçäàíèè ñòðàíèö. Áîëüøå èíôîðìàöèè î êîíòðîëëåðàõ âû ìîæåòå
      íàéòè â ãëàâå Êîíòðîëëåð .

      Íåîáÿçàòåëüíûé øàã 3: Ñîçäàíèå øàáëîíà

      Øàáëîíû ïîçâîëÿþò íàì âûíåñòè ðàçìåòêó ñòðàíèö (HTML êîä, êàê ïðàâèëî) â îòäåëüíûé ôàéë è ïîâòîðíî èñïîëüçîâàòü ðàçëè÷íûå ÷àñòè øàáëîíà ñòðàíèöû. Âìåñòî
      òîãî ÷òîáû ïèñàòü êîä âíóòðè êîíòðîëëåðà, âîñïîëüçóåìñÿ øàáëîíîì:
      1


      2
      3
      4

      // src/Acme/HelloBundle/Controller/HelloController.php
      namespace Acme\HelloBundle\Controller;

      5
      6

      use Symfony\Bundle\FrameworkBundle\Controller\Controller;

      7
      8

      class HelloController extends Controller
      2.4.

      Ñîçäàíèå ñòðàíèö â Symfony2

      47

      Symfony Documentation, Âûïóñê 2.0

      9

      {

      public function indexAction($name)
      {
      return $this->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));

      10
      11
      12
      13
      14
      15

      }

      16
      17

      }

      // render a PHP template instead
      // return $this->render('AcmeHelloBundle:Hello:index.html.php', array('name' => $name));

      Ïðèìå÷àíèå: Äëÿ òîãî, ÷òîáû èñïîëüçîâàòü ìåòîä render(), íåîáõîäèìî îòíàñëå-

      äîâàòüñÿ îò êëàññà Symfony\Bundle\FrameworkBundle\Controller\Controller (API
      docs: Symfony\Bundle\FrameworkBundle\Controller\Controller), êîòîðûé äîáàâëÿåò
      íåñêîëüêî ìåòîäîâ äëÿ áûñòðîãî âûçîâà ÷àñòî óïîòðåáëÿåìûõ ôóíêöèé êîíòðîëëåðà.
       ïðåäûäóùåì ïðèìåðå ýòî äîñòèãàåòñÿ ïóò¼ì äîáàâëåíèÿ âûðàæåíèÿ use â ñòðîêå 6
      è, çàòåì, íàñëåäîâàíèåì îò êëàññà Controller â ñòðîêå 8.
      Ìåòîä render() ñîçäàåò îáúåêò Response, çàïîëíåííûé ðåçóëüòàòîì îáðàáîòêè (ðåíäåðèíãà) øàáëîíà. Êàê è â ëþáîì äðóãîì êîíòðîëëåðå, âû, â êîíöå êîíöîâ, âåðíåòå
      îáúåêò Response.
      Îáðàòèòå âíèìàíèå, ÷òî åñòü äâå ðàçëè÷íûå âîçìîæíîñòè ðåíäåðèíãà øàáëîíîâ.
      Symfony2 ïî óìîë÷àíèþ, ïîääåðæèâàåò 2 ÿçûêà øàáëîíîâ: êëàññè÷åñêèå PHP-øàáëîíû
      è ïðîñòîé, íî ìîùíûé ÿçûê øàáëîíîâ Twig. Íî íå ïóãàéòåñü, âû ñâîáîäíû â âûáîðå òîãî
      èëè èíîãî èç íèõ, êðîìå òîãî âû ìîæåòå èñïîëüçîâàòü îáà â ðàìêàõ îäíîãî ïðîåêòà.
      Êîíòðîëëåð îòîáðàæàåò øàáëîí AcmeHelloBundle:Hello:index.html.twig, êîòîðûé
      íàçâàí ñ èñïîëüçîâàíèåì ñëåäóþùèõ ñîãëàøåíèé:

      BundleName:ControllerName:TemplateName
      Ýòî, òàê íàçûâàåìîå, ëîãè÷åñêîå èìÿ øàáëîíà, êîòîðîå ñîîòâåòñòâóåò ôèçè÷åñêîìó ôàéëó íà îñíîâàíèè ñëåäóþùèõ ñîãëàøåíèé:

      /ïóòü/ê/BundleName/Resources/views/ControllerName/TemplateName
       íàøåì ñëó÷àå AcmeHelloBundle - ýòî íàèìåíîâàíèå ïàêåòà, Hello - ýòî êîíòðîëëåð è
      index.html.twig - ýòî øàáëîí:
      ˆ
      1
      2

      Twig

      {# src/Acme/HelloBundle/Resources/views/Hello/index.html.twig #}
      {% extends '::base.html.twig' %}

      3
      4
      5
      6

      48

      {% block body %}
      Hello {{ name }} !
      {% endblock %}

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      PHP


      extend('::base.html.php') ?>
      Hello escape($name) ?> !

      Äàâàéòå ðàññìîòðèì ïîäðîáíåå øàáëîí Twig:
      ˆ

      Òîêåí extends îïðåäåëÿåò ðîäèòåëüñêèé øàáëîí. Òàêèì îáðàçîì, ñàì
      øàáëîí îäíîçíà÷íûì îáðàçîì îïðåäåëÿåò ðîäèòåëÿ (layout) âíóòðü êîòîðîãî îí
      áóäåò ïîìåùåí.

      ˆ

      ñòðîêà 4 : Òîêåí block îçíà÷àåò, ÷òî âñ¼ âíóòðè íåãî áóäåò ïîìåùåíî â áëîê ñ
      èìåíåì body. Êàê âû óâèäèòå íèæå, ýòî óæå îáÿçàííîñòü ðîäèòåëüñêîãî øàáëîíà
      (base.html.twig)  ïîëíîñòüþ îòîáðàçèòü áëîê body.

      ñòðîêà 2 :

      Ðîäèòåëüñêèé øàáëîí, ::base.html.twig, íå âêëþ÷àåò â ñåáÿ íè èìåíè ïàêåòà, íè
      èìåíè êîíòðîëëåðà (îòñþäà è äâîéíîå äâîåòî÷èå â íà÷àëå èìåíè (::)). Ýòî îçíà÷àåò,
      ÷òî øàáëîí ðàñïîëàãàåòñÿ âíå ïàêåòà â äèðåêòîðèè app:
      ˆ

      Twig

      {# app/Resources/views/base.html.twig #}




      {% block title %} Welcome!{% endblock %}
      {% block stylesheets %} {% endblock %}



      {% block body %} {% endblock %}
      {% block javascripts %} {% endblock %}



      ˆ

      PHP






      <?php $view['slots']->output('title', 'Welcome!') ?>
      output('stylesheets') ?>
      form:
      true
      csrf_protection: true
      validation:
      { enable_annotations: true }
      templating:
      { engines: ['twig'] } #assets_version: SomeVersionScheme
      session:
      default_locale: %locale%
      auto_start:
      true
      # Twig Configuration
      twig:
      debug:
      %kernel.debug%
      strict_variables: %kernel.debug%
      # ...

      ˆ

      XML






      56

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0
















      ˆ

      PHP

      $this->import('parameters.yml');
      $this->import('security.yml');
      $container->loadFromExtension('framework', array(
      'secret'
      => '%secret%',
      'charset'
      => 'UTF-8',
      'router'
      => array('resource' => '%kernel.root_dir%/config/routing.php'),
      'form'
      => array(),
      'csrf-protection' => array(),
      'validation'
      => array('annotations' => true),
      'templating'
      => array(
      'engines' => array('twig'),
      #'assets_version' => "SomeVersionScheme",
      ),
      'session' => array(
      'default_locale' => "%locale%",
      'auto_start'
      => true,
      ),
      ));
      // Twig Configuration
      $container->loadFromExtension('twig', array(
      'debug'
      => '%kernel.debug%',
      'strict_variables' => '%kernel.debug%',
      ));

      2.4.

      Ñîçäàíèå ñòðàíèö â Symfony2

      57

      Symfony Documentation, Âûïóñê 2.0

      // ...

      Ïðèìå÷àíèå: Ïîäðîáíåå î òîì, êàê çàãðóæàòü êàæäûé ôàéë/ôîðìàò áóäåò ðàññêà-

      çàíî â ñëåäóþùåé ñåêöèè - Îêðóæåíèÿ.

      Êàæäûé ïàðàìåòð âåðõíåãî óðîâíÿ, íàïðèìåð framework èëè twig, îïðåäåëÿåò íàñòðîéêè êîíêðåòíîãî ïàêåòà. Íàïðèìåð, êëþ÷ framework îïðåäåëÿåò íàñòðîéêè ÿäðà Symfony
      FrameworkBundle è âêëþ÷àåò íàñòðîéêè ìàðøðóòèçàöèè, øàáëîíèçàòîðà è ïðî÷èõ êëþ÷åâûõ ñèñòåì.
      Ïîêà æå íàì íå ñòîèò áåñïîêîèòüñÿ î êîíêðåòíûõ íàñòðîéêàõ â êàæäîé ñåêöèè. Ôàéë
      íàñòðîåê ïî óìîë÷àíèþ ñîäåðæèò âñå íåîáõîäèìûå ïàðàìåòðû. Ïî õîäó ÷òåíèÿ ïðî÷åé
      äîêóìåíòàöèè âû îçíàêîìèòåñü ñî âñåìè ñïåöèôè÷åñêèìè íàñòðîéêàìè.

      Ôîðìàòû êîíôèãóðàöèé
      Âî âñåõ ãëàâàõ êíèãè âñå ïðèìåðû êîíôèãóðàöèé áóäóò ïîêàçàíû âî âñåõ òðåõ
      ôîðìàòàõ (YAML, XML and PHP). Êàæäûé èç íèõ èìååò ñâîè äîñòîèíñòâà è íåäîñòàòêè. Âûáîð æå ôîðìàòà öåëèêîì çàâèñèò î âàøèõ ïðåäïî÷òåíèé:
      ˆ YAML: Ïðîñòîé, ïîíÿòíûé è ÷èòàáåëüíûé;
      ˆ XML:  ðàçû áîëåå ìîùíûé, íåæåëè YAML, ê òîìó æå ìíîãèå ñîâðåìåííûå
      IDE ïîääåðæèâàþò àâòîçàâåðøåíèå â XML;
      ˆ PHP : Î÷åíü ìîùíûé, íî ìåíåå ÷èòàáåëüíûé, ÷åì ñòàíäàðòíûå ôîðìàòû êîíôèãóðàöèîííûõ ôàéëîâ.

      2.4.5 Îêðóæåíèÿ

      Ïðèëîæåíèå ìîæíî çàïóñêàòü â ðàçëè÷íûõ îêðóæåíèÿõ. Ðàçëè÷íûå îêðóæåíèÿ èñïîëüçóþò îäèí è òîò æå PHP êîä (çà èñêëþ÷åíèåì ôðîíò-êîíòðîëëåðà), íî ìîãóò èìåòü
      ñîâåðøåííî ðàçëè÷íûå íàñòðîéêè. Íàïðèìåð, dev îêðóæåíèå âåäåò ëîã îøèáîê è çàìå÷àíèé, â òî âðåìÿ êàê prod îêðóæåíèå ëîããèðóåò òîëüêî îøèáêè.  dev íåêîòîðûå
      ôàéëû ïåðåñîçäàþòñÿ ïðè êàæäîì çàïðîñå, íî êýøèðóþòñÿ â prod îêðóæåíèè. Â òî æå
      âðåìÿ, âñå îêðóæåíèÿ îäíîâðåìåííî äîñòóïíû íà îäíîé è òîé æå ìàøèíå.
      Ïðîåêò Symfony2 ïî óìîë÷àíèþ èìååò òðè îêðóæåíèÿ (dev, test è prod), õîòÿ ñîçäàòü
      íîâîå îêðóæåíèå íå ñëîæíî. Âû ìîæåòå ñìîòðåòü âàøå ïðèëîæåíèå â ðàçëè÷íûõ îêðóæåíèÿõ ïðîñòî ìåíÿÿ ôðîíò-êîíòðîëëåðû â áðàóçåðå. Äëÿ òîãî ÷òîáû îòîáðàçèòü ïðèëîæåíèå â dev îêðóæåíèè, îòêðîéòå åãî ïðè ïîìîùè ôðîíò êîíòðîëëåðà app_dev.php:
      http://localhost/app_dev.php/hello/Ryan

      Åñëè æå âû õîòèòå ïîñìîòðåòü, êàê ïîâåä¼ò ñåáÿ ïðèëîæåíèå â ïðîäóêòîâîì îêðóæåíèè,
      âû ìîæåòå âûçâàòü ôðîíò-êîíòðîëëåð prod:
      58

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      http://localhost/app.php/hello/Ryan

      Òàê êàê prod îêðóæåíèå îïòèìèçèðîâàíî äëÿ ñêîðîñòè, íàñòðîéêè, ìàðøðóòû è øàáëîíû Twig êîìïèëèðóþòñÿ â ïëîñêèå PHP êëàññû è êýøèðóþòñÿ. Êîãäà âû õîòèòå
      ïîñìîòðåòü èçìåíåíèÿ â ïðîäóêòîâîì îêðóæåíèè, âàì ïîòðåáóåòñÿ óäàëèòü ýòè ôàéëû
      ÷òîáû îíè ïåðåñîçäàëèñü àâòîìàòè÷åñêè:
      php app/console cache:clear --env=prod --no-debug

      Ïðèìå÷àíèå: Åñëè âû îòêðîåòå ôàéë web/app.php, âû îáíàðóæèòå, ÷òî îí îäíîçíà÷íî
      íàñòðîåí íà èñïîëüçîâàíèå prod îêðóæåíèÿ:
      $kernel = new AppKernel('prod', false);

      Âû ìîæåòå ñîçäàòü íîâûé ôðîíò-êîíòðîëëåð äëÿ íîâîãî îêðóæåíèÿ ïðîñòî ñêîïèðîâàâ
      ýòîò ôàéë è èçìåíèâ prod íà äðóãîå çíà÷åíèå.

      Ïðèìå÷àíèå: Òåñòîâîå îêðóæåíèå (test) èñïîëüçóåòñÿ ïðè çàïóñêå àâòîòåñòîâ è åãî
      íåëüçÿ íàïðÿìóþ îòêðûòü ÷åðåç áðàóçåð. Ïîäðîáíåå îá ýòî ìîæíî ïî÷èòàòü â ãëàâå
      Òåñòèðîâàíèå .

      Íàñòðîéêà îêðóæåíèé

      Êëàññ AppKernel îòâå÷àåò çà çàãðóçêó êîíôèãóðàöèîííûõ ôàéëîâ:
      // app/AppKernel.php
      public function registerContainerConfiguration(LoaderInterface $loader)
      {
      $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
      }

      Âû óæå çíàåòå, ÷òî ðàñøèðåíèå .yml ìîæåò áûòü èçìåíåíî íà .xml èëè .php, åñëè âû
      ïðåäïî÷èòàåòå èñïîëüçîâàòü XML èëè PHP äëÿ ôàéëîâ êîíôèãóðàöèè. Èìåéòå òàêæå
      â âèäó, ÷òî êàæäîå îêðóæåíèå çàãðóæàåò ñâîè ñîáñòâåííûå íàñòðîéêè. Ðàññìîòðèì
      êîíôèãóðàöèîííûé ôàéë äëÿ dev îêðóæåíèÿ.
      ˆ

      YAML

      # app/config/config_dev.yml
      imports:
      - { resource: config.yml }

      2.4.

      Ñîçäàíèå ñòðàíèö â Symfony2

      59

      Symfony Documentation, Âûïóñê 2.0

      framework:
      router: { resource: "%kernel.root_dir%/config/routing_dev.yml" }
      profiler: { only_exceptions: false }
      # ...

      ˆ

      XML











      ˆ

      PHP

      // app/config/config_dev.php
      $loader->import('config.php');
      $container->loadFromExtension('framework', array(
      'router' => array('resource' => '%kernel.root_dir%/config/routing_dev.php'),
      'profiler' => array('only-exceptions' => false),
      ));
      // ...

      Êëþ÷ imports ïîõîæ ïî äåéñòâèþ íà âûðàæåíèå include â PHP è ãàðàíòèðóåò ÷òî ãëàâíûé êîíôèãóðàöèîííûé ôàéë (config.yml) áóäåò çàãðóæåí â ïåðâóþ î÷åðåäü. Îñòàëüíîé êîä êîððåêòèðóåò êîíôèãóðàöèþ ïî óìîë÷àíèþ äëÿ óâåëè÷åíèÿ ïîðîãà ëîããèðîâàíèÿ è ïðî÷èõ íàñòðîåê, ñïåöèôè÷íûõ äëÿ ïðîöåññà ðàçðàáîòêè.
      Îáà îêðóæåíèÿ  prod è test ñëåäóþò òîé æå ìîäåëè: êàæäîå îêðóæåíèå èìïîðòèðóåò áàçîâûå íàñòðîéêè è ìîäèôèöèðóåò èõ çíà÷åíèÿ äëÿ ñâîèõ íóæä. Ýòî ñîãëàøåíèå
      ïîçâîëÿåò ïîâòîðíî èñïîëüçîâàòü áîëüøóþ ÷àñòü íàñòðîåê è èçìåíÿòü ëèøü òå èç íèõ,
      êîòîðûå òðåáóåò îêðóæåíèå.

      60

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      2.4.6 Çàêëþ÷åíèå

      Ïîçäðàâëÿåì! Âû óñâîèëè âñå ôóíäàìåíòàëüíûå àñïåêòû Symfony2 è îáíàðóæèëè, êàêèìè ë¼ãêèìè è â òî æå âðåìÿ ãèáêèìè îíè ìîãóò áûòü. È, ïîñêîëüêó íà ïîäõîäå åù¼
      ìíîãî èíòåðåñíîãî, îáÿçàòåëüíî çàïîìíèòå ñëåäóþùèå ïîëîæåíèÿ:
      ˆ ñîçäàíèå ñòðàíèö  ýòî òðè ïðîñòûõ øàãà, âêëþ÷àþùèõ ìàðøðóò, êîíòðîëëåð
      è (îïöèîíàëüíî) øàáëîí;
      ˆ êàæäîå ïðèëîæåíèå äîëæíî ñîäåðæàòü 4 îñíîâíûõ äèðåêòîðèè: web/ (àññåòû è
      ôðîíò-êîíòðîëëåðû), app/ (íàñòðîéêè), src/ (âàøè ïàêåòû), è vendor/ (ñòîðîííèå áèáëèîòåêè) (òàêæå åù¼ èìååòñÿ äèðåêòîðèÿ bin/ äëÿ îáíîâëåíèÿ âåíäîðîâ);
      ˆ Êàæäàÿ ôóíêöèÿ â Symfony2 (âêëþ÷àÿ ÿäðî ôðåéìâîðêà) äîëæíà ðàñïîëàãàòüñÿ
      âíóòðè ïàêåòà, êîòîðûé ïðåäñòàâëÿåò ñîáîé ñòðóêòóðèðîâàííûé íàáîð ôàéëîâ,
      ðåàëèçóþùèõ ýòó ôóíêöèþ;
      ˆ

      êàæäîãî ïàêåòà ðàñïîëàãàþòñÿ â äèðåêòîðèè app/config è ìîãóò áûòü
      çàïèñàíû â ôîðìàòå YAML, XML èëè PHP;
      íàñòðîéêè

      ˆ êàæäîå îêðóæåíèå äîñòóïíî ÷åðåç ñâîé îòäåëüíûé ôðîíò-êîíòðîëëåð (íàïðèìåð
      app.php è app_dev.php) è çàãðóæàåò îòäåëüíûé ôàéë íàñòðîåê;
      Äàëåå, êàæäàÿ ãëàâà êíèãè ïîçíàêîìèò âàñ ñ âñå áîëåå è áîëåå ìîùíûìè èíñòðóìåíòàìè
      è áîëåå ãëóáîêèìè êîíöåïöèÿìè. ×åì áîëüøå âû çíàåòå î Symfony2, òåì áîëüøå âû
      áóäåòå öåíèòü ãèáêîñòü åãî àðõèòåêòóðû è åãî îáøèðíûå âîçìîæíîñòè äëÿ áûñòðîé
      ðàçðàáîòêè ïðèëîæåíèé.

      2.5 Êîíòðîëëåð
      Êîíòðîëëåð - ýòî PHP-ôóíêöèÿ, êîòîðóþ âû ñîçäà¼òå, ÷òîáû ïîëó÷èòü èíôîðìàöèþ èç
      HTTP çàïðîñà è íà å¼ îñíîâå ñîçäàòü HTTP îòâåò â âèäå îáúåêòà Response. Îòâåò ìîæåò áûòü HTML ñòðàíèöåé, XML-äîêóìåíòîì, ñåðèàëèçîâàííûì JSON-ìàññèâîì, èçîáðàæåíèåì, ïåðåíàïðàâëåíèåì, îøèáêîé 404, âñåì ÷åì óãîäíî, î ÷¼ì âû òîëüêî ìîãëè
      ìå÷òàòü. Êîíòðîëëåð ñîäåðæèò ëþáóþ ëîãèêó âàøåãî ïðèëîæåíèÿ, íåîáõîäèìóþ äëÿ
      òîãî, ÷òîáû îòîáðàçèòü ñîäåðæèìîå ñòðàíèöû.
      Äëÿ òîãî ÷òîáû óâèäåòü, íàñêîëüêî ïðîñòî ýòîãî ìîæíî äîáèòüñÿ, äàâàéòå ðàññìîòðèì
      êîíòðîëëåð Symfony2 â äåéñòâèè. Ñëåäóþùèé êîíòðîëëåð îòîáðàçèò ñòðàíèöó, êîòîðàÿ
      âñåãî-íàâñåãî íàïå÷àòàåò Hello world!:
      use Symfony\Component\HttpFoundation\Response;
      public function helloAction()
      {
      2.5.

      Êîíòðîëëåð

      61

      Symfony Documentation, Âûïóñê 2.0

      }

      return new Response('Hello world!');

      Öåëü ó êîíòðîëëåðà âñåãäà îäíà: ñîçäàòü è âåðíóòü îáúåêò Response. Ñëåäóÿ ýòîé öåëè,
      êîíòðîëëåð ìîæåò ÷èòàòü èíôîðìàöèþ èç çàïðîñà, çàãðóæàòü ðåñóðñû èç áàçû äàííûõ,
      îòïðàâëÿòü email èëè æå çàïèñûâàòü èíôîðìàöèþþ â ñåññèþ ïîëüçîâàòåëÿ. Íî âñåãäà, â
      êîíå÷íîì èòîãå, êîíòðîëëåð âåðí¼ò îáúåêò Response, êîòîðûé áóäåò îòïðàâëåí êëèåíòó.
      Çäåñü íåò íèêàêîé ìàãèè èëè äðóãèõ òðåáîâàíèé, î êîòîðûõ ñòîèëî áû áåñïîêîèòüñÿ!
      Âîò íåñêîëüêî òèïè÷íûõ ïðèìåðîâ:
      ˆ

      Êîíòðîëëåð A

      ˆ

      Êîíòðîëëåð B

      ˆ

      Êîíòðîëëåð C

      íèöû ñàéòà.

      ñîçäà¼ò îáúåêò Response, ñîäåðæàùèé êîíòåíò äëÿ ãëàâíîé ñòðà-

      ïîëó÷àåò èç çàïðîñà ïàðàìåòð slug äëÿ òîãî, ÷òîáû çàãðóçèòü
      çàïèñü áëîãà èç áàçû äàííûõ è ñîçäàòü îáúåêò Response, îòîáðàæàþùèé ýòîò
      áëîã. Åñëè óêàçàííûé slug íå ìîæåò áûòü íàéäåí â áàçå, îí ñîçäà¼ò è âîçâðàùàåò
      îáúåêò Response ñî ñòàòóñ-êîäîì 404 (íå íàéäåíî).
      îáðàáàòûâàåò îòïðàâëåííóþ ôîðìó êîíòàêòîâ. Îí ÷èòàåò èíôîðìàöèþ î ôîðìå èç çàïðîñà, ñîõðàíÿåò êîíòàêòíóþ èíôîðìàöèþ â áàçó äàííûõ è
      îòïðàâëÿåò ñîîáùåíèå âåáìàñòåðó. Íàêîíåö, îí ñîçäà¼ò îáúåêò Response, êîòîðûé
      ïåðåíàïðàâëÿåò áðàóçåð êëèåíòà íà ñòðàíèöó thank you.

      2.5.1 Æèçíåííûé öèêë Çàïðîñ-Êîíòðîëëåð-Îòâåò

      Êàæäûé çàïðîñ, îáðàáàòûâàåìûé ïðîåêòîì Symfony2, ñëåäóåò îäíîìó è òîìó æå ïðîñòîìó æèçíåííîìó öèêëó. Ôðåéìâîðê áåð¼ò íà ñåáÿ ïîâòîðÿþùèåñÿ çàäà÷è è, â êîíöå
      êîíöîâ âûïîëíÿåò êîíòðîëëåð, êîòîðûé ñîäåðæèò êîä âàøåãî ïðèëîæåíèÿ:
      1. Êàæäûé çàïðîñ îáðàáàòûâàåòñÿ îäíèì ôðîíò-êîíòðîëëåðîì (íàïðèìåð app.php
      èëè app_dev.php), êîòîðûé çàãðóæàåò ïðèëîæåíèå;
      2. Router ÷èòàåò èíôîðìàöèþ èç çàïðîñà (URI ê ïðèìåðó), èùåò ïîäõîäÿùèé ìàðøðóò è ïîëó÷àåò ïàðàìåòð _controller èç ìàðøðóòà;
      3. Êîíòðîëëåð, ñîîòâåòñòâóþùèé ìàðøðóòó, âûïîëíÿåòñÿ è åãî êîä ôîðìèðóåò îáúåêò Response;
      4. HTTP-çàãîëîâêè è êîíòåíò îáúåêòà Response îòïðàâëÿþòñÿ îáðàòíî êëèåíòó, îòïðàâèâøåìó èçíà÷àëüíûé çàïðîñ.
      Ñîçäàíèå ñòðàíèöû - ýòî ïî ñóòè ñîçäàíèå êîíòðîëëåðà (#3) è ìàðøðóòà, êîòîðûé
      ñòàâèò â ñîîòâåòñòâèå êîíòðîëëåðó íåêèé URL (#2).

      Ïðèìå÷àíèå: Íå ñìîòðÿ íà òî ÷òî ôðîíò-êîíòðîëëåð è êîíòðîëëåð íàçâàíû ïî-

      õîæèì îáðàçîì, îíè ñèëüíî ðàçëè÷àþòñÿ - îá ýòîì ìû åùå ïîãîâîðèì ÷óòü ïîçæå â

      62

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ýòîé ãëàâå. Ôðîíò-êîíòðîëëåð - ýòî êîðîòêèé PHP-ôàéë, êîòîðûé íàõîäèòñÿ â webäèðåêòîðèè è êîòîðûé îáðàáàòûâàåò âñå âõîäÿùèå çàïðîñû. Òèïè÷íîå ïðèëîæåíèå èìååò ïðîäóêòîâûé êîíòðîëëåð (prod, êàê ïðàâèëî app.php) è êîíòðîëëåð äëÿ ðàçðàáîòêè
      (dev, êàê ïðàâèëî app_dev.php). È âàì ñêîðåå âñåãî íèêîãäà íå ïðèäåòñÿ ìîäèôèöèðîâàòü èëè âîîáùå çàäóìûâàòüñÿ î ôðîíò-êîíòðîëëåðàõ â âàøåì ïðèëîæåíèè.

      2.5.2 Ïðîñòîé êîíòðîëëåð

      Â òî âðåìÿ êàê êîíòðîëëåð ìîæåò áûòü ëþáîé PHP-ñóùíîñòüþ, êîòîðóþ ìîæíî âûçâàòü
      (ôóíêöèåé, ìåòîäîì îáúåêòà, èëè æå çàìûêàíèåì (Closure)), â Symfony2 êîíòðîëëåð
      - ýòî êàê ïðàâèëî íåêèé ìåòîä îáúåêòà êîíòðîëëåðà. Êîíòðîëëåðû òàêæå íàçûâàþòñÿ
      äåéñòâèÿìè (actions).
      1


      2
      3

      // src/Acme/HelloBundle/Controller/HelloController.php

      4
      5
      6

      namespace Acme\HelloBundle\Controller;
      use Symfony\Component\HttpFoundation\Response;

      7
      8
      9
      10
      11
      12
      13
      14

      class HelloController
      {
      public function indexAction($name)
      {
      return new Response('Hello '.$name.'!');
      }
      }

      Ñîâåò: Îáðàòèòå âíèìàíèå, ÷òî êîíòðîëëåð - ýòî ìåòîä indexAction, êîòîðûé ðàñïîëî-

      æåí âíóòðè êëàññà êîíòðîëëåðà (HelloController). Ñìîòðèòå íå ïóòàéòåñü: êëàññ êîíòðîëëåðà - ýòî ïðîñòî óäîáíûé ñïîñîá ñãðóïïèðîâàòü íåñêîëüêî êîíòðîëëåðîâ/äåéñòâèé
      âìåñòå. Îáû÷íî êëàññ êîíòðîëëåðà ñîäåðæèò íåñêîëüêî êîíòðîëëåðîâ/äåéñòâèé (íàïðèìåð updateAction, deleteAction è ò.ä.).
      Ýòîì êîíòðîëëåðå íåò íè÷åãî ñëîæíîãî, íî äàâàéòå ðàçáåð¼ì ïîäðîáíåå:
      ˆ

      ñòðîêà 5 :

      ˆ

      Èìÿ êëàññà ýòî ðåçóëüòàò îáúåäèíåíèÿ èìåíè êîíòðîëëåðà (Hello) è
      ñëîâà Controller. Ýòî î÷åðåäíàÿ äîãîâîð¼ííîñòü, êîòîðàÿ ïîçâîëÿåò îáåñïå÷èòü
      åäèíîîáðàçèå â èìåíîâàíèè êîíòðîëëåðîâ è ïîçâîëÿåò ññûëàòüñÿ íà êëàññ òîëüêî
      ïî ïåðâîé ÷àñòè íàèìåíîâàíèÿ (çäåñü ýòî áóäåò Hello) â êîíôèãóðàöèè ìàðøðóòèçàòîðà.

      2.5.

      Symfony2 èñïîëüçóåò ïðåèìóùåñòâà ïðîñòðàíñòâ èì¼í PHP 5.3. Êëþ÷åâîå ñëîâî use èìïîðòèðóåò êëàññ Response, êîòîðûé êîíòðîëëåð äîëæåí âåðíóòü.
      ñòðîêà 8 :

      Êîíòðîëëåð

      63

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      Êàæäîå äåéñòâèå â êëàññå êîíòðîëëåðà èìååò ñóôôèêñ Action è óïîìèíàåòñÿ â íàñòðîéêàõ ìàðøðóòèçàòîðà òîëüêî ïî èìåíè (index). Â ñëåäóþùåé ñåêöèè
      âû ñîçäàäèòå ìàðøðóò, êîòîðûé ïðèâÿæåò URI ê ýòîìó äåéñòâèþ. Âû óçíàåòå êàê
      çàïîëíèòåëü äëÿ èìåíè â ìàðøðóòå - {name} - ñòàíåò àðãóìåíòîì ìåòîäà äåéñòâèÿ
      ($name).

      ˆ

      line 12 :

      line 10 :

      Êîíòðîëëåð ñîçäà¼ò è âîçâðàùàåò îáúåêò Response.

      2.5.3 Ñîîòâåòñòâèå URL Êîíòðîëëåðó

      Íîâûé êîíòðîëëåð âîçâðàùàåò ïðîñòóþ HTML-ñòðàíèöó. Äëÿ òîãî ÷òîáû óâèäåòü ýòó
      ñòðàíèöó â âàøåì áðàóçåðå, âàì íàäî ñîçäàòü ìàðøðóò, êîòîðûé óñòàíàâëèâàåò ñîîòâåòñòâèå ìåæäó íåêîòîðûì øàáëîíîì URL è êîíòðîëëåðîì:
      ˆ

      YAML

      # app/config/routing.yml
      hello:
      pattern:
      /hello/{name}
      defaults:
      { _controller: AcmeHelloBundle:Hello:index }

      ˆ

      XML



      AcmeHelloBundle:Hello:index


      ˆ

      PHP

      // app/config/routing.php
      $collection->add('hello', new Route('/hello/{name}', array(
      '_controller' => 'AcmeHelloBundle:Hello:index',
      )));

      /hello/ryan òåïåðü âûïîëíÿåòñÿ êîíòðîëëåð
      HelloController::indexAction() è ïðèñâàèâàåò ïåðåìåííîé $name çíà÷åíèå ryan.
      Ñîçäàíèå ñòðàíèöû ïî ñóòè ïîäðàçóìåâàåò âñåãî ëèøü ñîçäàíèå ìåòîäà êîíòðîëëåðà è
      ñîîòâåòñòâóþùåãî ìàðøðóòà.
      Òåïåðü

      ïðè

      çàïðîñå

      URI

      Îáðàòèòå âíèìàíèå íà ñèíòàêñèñ, ïðè ïîìîùè êîòîðîãî ìàðøðóò ññûëàåòñÿ íà êîíòðîëëåð: AcmeHelloBundle:Hello:index. Symfony2 èñïîëüçóåò ïðîñòóþ ñòðîêîâóþ íîòàöèþ
      äëÿ ñîçäàíèÿ ññûëîê íà ðàçëè÷íûå êîíòðîëëåðû. Ýòîò î÷åíü ïðîñòîé ñèíòàêñèñ ñîîáùàåò Symfony2 ÷òî êëàññ êîíòðîëëåðà ñ èìåíåì HelloController ðàñïîëîæåí â ïàêåòå
      AcmeHelloBundle. Çàòåì âûïîëíÿåòñÿ ìåòîä indexAction().
      Áîëåå ïîäðîáíî î ôîðìàòå ñòðîê, èñïîëüçóåìûõ äëÿ ñîçäàíèÿ ññûëîê íà ðàçëè÷íûå
      êîíòðîëëåðû ìîæíî ïî÷èòàòü çäåñü: Øàáëîí Èìåíîâàíèÿ Êîíòðîëëåðà .
      64

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Ïðèìå÷àíèå:  ýòîì ïðèìåðå êîíôèãóðàöèÿ ìàðøðóòèçàòîðà âûïîëíÿåòñÿ íåïîñðåäñòâåííî â äèðåêòîðèè app/config/. Íà ïðàêòèêå áîëåå óäîáåí ñïîñîá, êîãäà âàøè ìàðøðóòû ðàçìåùàþòñÿ â ïàêåòå, êîòîðîìó ñîîòâåòñòâóþò. Áîëåå ïîäðîáíî ýòîò ñïîñîá ðàññìàòðèâàåòñÿ çäåñü: Ïîäêëþ÷åíèå âíåøíèõ ðåñóðñîâ äëÿ ìàðøðóòèçàöèè .
      Ñîâåò: Ïîäðîáíî âîïðîñû ìàðøðóòèçàöèè ðàññìàòðèâàþòñÿ â ãëàâå Ìàðøðóòèçàöèÿ .

      Ïàðàìåòðû ìàðøðóòà â êà÷åñòâå àðãóìåíòîâ Êîíòðîëëåðà

      Âû óæå çíàåòå, ÷òî ïàðàìåòð _controller ñî çíà÷åíèåì AcmeHelloBundle:Hello:index
      ññûëàåòñÿ íà ìåòîä HelloController::indexAction(), êîòîðûé ðàñïîëîæåí â ïàêåòå
      AcmeHelloBundle. Òàêæå èíòåðåñ ïðåäñòàâëÿþò àðãóìåíòû, êîòîðûå ïåðåäàþòñÿ â ýòîò
      ìåòîä:
      // src/Acme/HelloBundle/Controller/HelloController.php
      namespace Acme\HelloBundle\Controller;
      use Symfony\Bundle\FrameworkBundle\Controller\Controller;
      class HelloController extends Controller
      {
      public function indexAction($name)
      {
      // ...
      }
      }

      Êîíòðîëëåð èìååò åäèíñòâåííûé àðãóìåíò - $name, êîòîðûé ñîîòâåòñòâóåò ïàðàìåòðó
      {name} èç ìàðøðóòà (â íàøåì ïðèìåðå - ryan). Ôàêòè÷åñêè, êîãäà êîíòðîëëåð âûïîëíÿåòñÿ, Symfony2 êàæäîìó àðãóìåíòó êîíòðîëëåðà ñòàâèò â ñîîòâåòñòâèå ïàðàìåòð èç
      ìàðøðóòà. Âçãëÿíèòå íà ïðèìåð:
      ˆ

      YAML

      # app/config/routing.yml
      hello:
      pattern:
      /hello/{first_name}/{last_name}
      defaults:
      { _controller: AcmeHelloBundle:Hello:index, color: green }

      ˆ

      2.5.

      XML

      Êîíòðîëëåð

      65

      Symfony Documentation, Âûïóñê 2.0



      AcmeHelloBundle:Hello:index
      green


      ˆ

      PHP

      // app/config/routing.php
      $collection->add('hello', new Route('/hello/{first_name}/{last_name}', array(
      '_controller' => 'AcmeHelloBundle:Hello:index',
      'color'
      => 'green',
      )));

      Êîíòðîëëåð äëÿ ýòîãî ïðèìåðà ïðèíèìàåò íåñêîëüêî àðãóìåíòîâ:
      public function indexAction($first_name, $last_name, $color)
      {
      // ...
      }

      âíèìàíèå, ÷òî îáà çàïîëíèòåëÿ äëÿ ïåðåìåííûõ ({first_name},
      {last_name}), êàê è ïåðåìåííàÿ ïî óìîë÷àíèþ color - äîñòóïíû â êà÷åñòâå àðãóìåíòîâ
      â êîíòðîëëåðå. Êîãäà ñîâïàäàåò ìàðøðóò, çàïîëíèòåëè ïåðåìåííûõ îáúåäèíÿþòñÿ ñ
      defaults â îäèí ìàññèâ, êîòîðûé ñòàíîâèòñÿ äîñòóïåí â âàøåì êîíòðîëëåðå.
      Îáðàòèòå

      Íàñòðîéêà ñîîòâåòñòâèÿ ïàðàìåòðîâ ìàðøðóòà àðãóìåíòàì êîíòðîëëåðà ïðîñòà, íóæíî
      ëèøü ñëåäîâàòü íèæåïåðå÷èñëåííûì ðåêîìåíäàöèÿì âî âðåìÿ ðàçðàáîòêè:
      ˆ Ïîðÿäîê àðãóìåíòîâ êîíòðîëëåðà íå èìååò çíà÷åíèÿ
      Symfony â ñîñòîÿíèè óñòàíîâèòü ñîîòâåòñòâèå ìåæäó èìåíàìè ïàðàìåòðîâ ìàðøðóòà è ñèãíàòóðîé ìåòîäà â êîíòðîëëåðå. Äðóãèìè ñëîâàìè,
      ýòî ðàáîòàåò òàêèì îáðàçîì, ÷òî ïàðàìåòð {last_name} ñîîòâåòñòâóåò
      àðãóìåíòó $last_name. Àðãóìåíòû êîíòðîëëåðà ìåíÿòü ìåñòàìè è îí
      âñ¼ ðàâíî áóäåò ðàáîòàòü:
      public function indexAction($last_name, $color, $first_name)
      {
      // ..
      }

      ˆ Êàæäûé îáÿçàòåëüíûé àðãóìåíò êîíòðîëëåðà äîëæåí ñîîòâåòñòâîâàòü

      ïàðàìåòðó ìàðøðóòà

      Ñëåäóþùèé ïðèìåð âûçîâåò èñêëþ÷åíèå RuntimeException, òàê êàê â
      ìàðøðóòå íå îïðåäåë¼í ïàðàìåòð foo:
      66

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      public function indexAction($first_name, $last_name, $color, $foo)
      {
      // ..
      }

      Äëÿ òîãî ÷òîáû ýòî ðàáîòàëî, íóæíî ñäåëàòü ïàðàìåòð îïöèîíàëüíûì.
      Ñëåäóþùèé ïðèìåð íå áóäåò âûçûâàòü èñêëþ÷èòåëüíîé ñèòóàöèè:
      public function indexAction($first_name, $last_name, $color, $foo = 'bar')
      {
      // ..
      }

      ˆ Ïàðàìåòðû ìàðøðóòà íå îáÿçàòåëüíî äîëæíû áûòü ïðåäñòàâëåíû â âè-

      äå àðãóìåíòîâ êîíòðîëëåðà

      Åñëè, ê ïðèìåðó, ïàðàìåòð last_name íå íóæåí â êîíòðîëëåðå, åãî ìîæíî îïóñòèòü:
      public function indexAction($first_name, $color)
      {
      // ..
      }

      Ñîâåò: Êàæäûé ìàðøðóò èìååò ñïåöèàëèçèðîâííûé ïàðàìåòð _route, êîòîðûé ñîäåð-

      æèò çíà÷åíèå ðàâíîå åãî èìåíè (íàïðèìåð hello). Îáû÷íî ýòî çíà÷åíèå íå èñïîëüçóåòñÿ,
      íî, òåì íå ìåíåå, ýòîò ïàðàìåòð òàêæå äîñòóïåí â êà÷åñòâå àðãóìåíòà êîíòðîëëåðà.

      Request

      êàê àðãóìåíò Êîíòðîëëåðà

      Äëÿ áîëüøåãî óäîáñòâà, âû òàêæå ìîæåòå ïåðåäàòü îáúåêò Request â êà÷åñòâå àðãóìåíòà â âàø êîíòðîëëåð. Ýòî îñîáåííî óäîáíî ïðè ðàáîòå ñ ôîðìàìè:
      use Symfony\Component\HttpFoundation\Request;
      public function updateAction(Request $request)
      {
      $form = $this->createForm(...);

      }
      2.5.

      $form->bindRequest($request);
      // ...

      Êîíòðîëëåð

      67

      Symfony Documentation, Âûïóñê 2.0

      2.5.4 Áàçîâûé êëàññ êîíòðîëëåðà

      Symfony2 âêëþ÷àåò áàçîâûé êëàññ Controller, êîòîðûé îêàçûâàåò ïîìîùü â âûïîëíåíèè íàèáîëåå òèïè÷íûõ çàäà÷ êîíòðîëëåðà è ïðåäîñòàâëÿåò âàøåìó êîíòðîëëåðó äîñòóï
      ê ëþáîìó ðåñóðñó, êîòîðûé ìîæåò ïîðòðåáîâàòüñÿ. Îñóùåñòâëÿÿ íàñëåäîâàíèå îò êëàññà Controller âû ïîëó÷èòå â ñâî¼ ðàñïîðÿæåíèå íåêîòîðîå ÷èñëî ìåòîäîâ-ïîìîùíèêîâ.
      Äîáàâüòå âûðàæåíèå use â íà÷àëå êëàññà êîíòðîëëåðà
      HelloController, ÷òîáû îí íàñëåäîâàëñÿ îò Controller:

      è

      ìîäèôèöèðóéòå

      // src/Acme/HelloBundle/Controller/HelloController.php
      namespace Acme\HelloBundle\Controller;
      use Symfony\Bundle\FrameworkBundle\Controller\Controller;
      use Symfony\Component\HttpFoundation\Response;
      class HelloController extends Controller
      {
      public function indexAction($name)
      {
      return new Response('Hello '.$name.'!');
      }
      }

      Ýòè èçìåíåíèÿ íà ñàìîì äåëå íè÷åãî íå ìåíÿþò â ëîãèêå ðàáîòû âàøåãî êîíòðîëëåðà.  ñëåäóþùåé ñåêöèè âû óçíàåòå î òåõ ìåòîäàõ-ïîìîùíèêàõ, êîòîðûå
      ïðåäîñòàâëÿåò áàçîâûé êëàññ. Ýòè ìåòîäû ïî ñóòè ÿâëÿþòñÿ îá¼ðòêàìè äëÿ áàçîâîãî ôóíêöèîíàëà Symfony2 êîòîðûé äîñòóïåí âàì â ëþáîì ñëó÷àå - ñ èñïîëüçîâàíèåì áàçîâîãî êëàññà Controller èëè æå áåç íåãî. Ñàìûé ëó÷øèé ïóòü
      äëÿ òîãî ÷òîáû óâèäåòü áàçîâûå ôóíêöèè â äåéñòâèè - çàãëÿíóòü â êîä êëàññà
      Symfony\Bundle\FrameworkBundle\Controller\Controller ñàìîñòîÿòåëüíî.

      Ñîâåò: Íàñëåäîâàíèå îò áàçîâîãî êëàññà ñîâåðøåííî íå îáÿçàòåëüíî â Symfony2. Ýòîò

      êàññ ñîäåðæèò óäîáíûå ìåòîäû-ÿðëûêè, íî íè÷åãî îáÿçàòåëüíîãî. Âû òàêæå ìîæåòå
      îòíàñëåäîâàòüñÿ îò êëàññà Symfony\Component\DependencyInjection\ContainerAware.
      Îáúåêò service container'à áóäåò äîñòóïåí ÷åðç ñâîéñòâî container.

      Ïðèìå÷àíèå:

      Âû òàêæå ìîæåòå îáúÿâèòü êîíòðîëëåð â êà÷åñòâå ñåðâèñà:
      .

      68

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      2.5.5 Êîíòðîëëåð, Áàçîâûå îïåðàöèè

      Õîòÿ, âèðòóàëüíî êîíòðîëëåð íè÷åãî äåëàòü íå îáÿçàí, â îñíîâíîì êîíòðîëëåðû âûïîëíÿþò îäíè è òå æå çàäà÷è ñíîâà è ñíîâà. Ýòè çàäà÷è, òàêèå êàê ïåðåíàïðàâëåíèå,
      ïåðåàäðåñàöèÿ, îòîáðàæåíèå øàáëîíà è äîñòóï ê îñíîâíûì ñåðâèñàì, â Symfony2 âûïîëíÿòü î÷åíü ëåãêî.
      Ïåðåíàïðàâëåíèå (redirecting)

      Åñëè âû õîòèòå ïåðåíàïðàâèòü ïîëüçîâàòåëÿ íà äðóãóþ ñòðàíèöó, èñïîëüçóéòå ìåòîä
      redirect():
      public function indexAction()
      {
      return $this->redirect($this->generateUrl('homepage'));
      }

      Ìåòîä generateUrl(), ýòî âñåãî-ëèøü ôóíêöèÿ ïîìîùíèê, êîòîðàÿ ãåíåðèðóåò URL
      äëÿ çàäàííîãî ìàðøðóòà. Áîëåå ïîäðîáíî ýòîò âîïðîñ ðàññìàòðèâàåòñÿ â ãëàâå Ìàðøðóòèçàöèÿ .
      Ïî óìîë÷àíèþ, ìåòîä redirect() âûïîëíÿåò ïåðåíàïðàâëåíèå ñ HTTP ñòàòóñ-êîäîì 302
      (âðåìåííîå ïåðåíàïðàâëåíèå). Äëÿ òîãî, ÷òîáû âûïîëíèòü ïîñòîÿííîå ïåðåíàïðàâëåíèå
      (ñî ñòàòóñ-êîäîì 301), íåîáõîäèìî äîáàâèòü âòîðîé àðãóìåíò:
      public function indexAction()
      {
      return $this->redirect($this->generateUrl('homepage'), 301);
      }

      Ñîâåò: Ìåòîä redirect() - ýòî ïðîñòî ÿðëû÷îê äëÿ îïåðàöèè ñîçäàíèÿ îáúåêòà
      Response, êîòîðûé ñïåöèàëèçèðóåòñÿ íà ïåðåíàïðàâëåíèè ïîëüçîâàòåëÿ. Îí ýêâèâàëåíòåí ñëåäóþùåìó êîäó:
      use Symfony\Component\HttpFoundation\RedirectResponse;
      return new RedirectResponse($this->generateUrl('homepage'));

      2.5.

      Êîíòðîëëåð

      69

      Symfony Documentation, Âûïóñê 2.0

      Êîíòðîëëåð, Ïåðåàäðåñàöèÿ (forwarding)

      Âû òàêæå ëåãêî ìîæåòå ïåðåàäðåñîâàòü çàïðîñ íà äðóãîé êîíòðîëëåð âíóòðè ñèñòåìû,
      èñïîëüçóÿ ìåòîä forward(). Âìåñòî òîãî, ÷òîáû âûïîëíèòü ïåðåíàïðàâëåíèå áðàóçåðà ïîëüçîâàòåëÿ, ýòîò ìåòîä âûïîëíÿåò âíóòðåííèé ïîäçàïðîñ è âûçûâàåò óêàçàííûé
      êîíòðîëëåð. Ìåòîä forward() âîçâðàùàåò îáúåêò Response, êîòîðûé âîçâðàùàåò êîíòðîëëåð, íà êîòîðûé îñóùåñòâëÿëàñü ïåðåàäðåñàöèÿ:
      public function
      {
      $response =
      'name'
      'color'
      ));

      indexAction($name)
      $this->forward('AcmeHelloBundle:Hello:fancy', array(
      => $name,
      => 'green'

      // Çäåñü ìîæíî ìîäèôèöèðîâàòü $response èëè æå ñðàçó âåðíóòü åãî ïîëüçîâàòåëþ
      }

      return $response;

      Îáðàòèòå âíèìàíèå, ÷òî ìåòîä forward() èñïîëüçóåò äëÿ óêàçàíèÿ êîíòðîëëåðà òîò æå
      ôîðìàò ñòðîêè, êîòîðûé èñïîëüçóåòñÿ â êîíôèãóðàöèè ìàðøðóòîâ. Òàêèì îáðàçîì,
      öåëüþ ïåðåàäðåñàöèè áóäåò HelloController èç ïàêåòà AcmeHelloBundle. Ìàññèâ, ïåðåäàâàåìûé ìåòîäó â êà÷åñòâå ïàðàìåòðà, áóäåò êîíâåðòèðîâàí â ïàðàìåòðû öåëåâîãî
      êîíòðîëëåðà. Òàêîé æå èíòåðôåéñ èñïîëüçóåòñÿ ïðè âñòðàèâàíèè êîíòðîëëåðîâ â øàáëîíû (ñì. Âíåäðåíèå êîíòðîëëåðîâ ). Ìåòîä öåëåâîãî êîíòðîëëåðà äîëæåí âûãëÿäåòü
      ñëåäóþùèì îáðàçîì:
      public function fancyAction($name, $color)
      {
      // ... create and return a Response object
      }

      È, êàê è â ñëó÷àå ñîçäàíèÿ êîíòðîëëåðà äëÿ ìàðøðóòà, ïîðÿäîê àðãóìåíòîâ äëÿ
      fancyAction íå èìååò çíà÷åíèÿ. Symfony2 óñòàíàâëèâàåò ñîîòâåòñòâèå ïî èìåíàì êëþ÷åé (íàïðèìåð name) è èìåíàì ïàðàìåòðîâ (íàïðèìåð $name). Åñëè âû èçìåíÿåòå ïîðÿäîê ñëåäîâàíèÿ àðãóìåíòîâ, Symfony2 òàêæå áóäåò ïðèñâàèâàòü âåðíûå çíà÷åíèÿ êàæäîé ïåðåìåííîé.

      Ñîâåò: Êàê è ïðî÷èå ìåòîäû áàçîâîãî êîíòðîëëåðà, ìåòîä forward - ýòî ïðîñòî ÿðëûê
      ê áàçîâîìó ôóíêöèîíàëó Symfony2. Ïåðåàäðåñàöèÿ ìîæåò áûòü âûïîëíåíà íàïðÿìóþ
      ÷åðåç ñåðâèñ http_kernel. Ïðè ïåðåàäðåñàöèè âîçâðàùàåòñÿ îáúåêò Response:
      $httpKernel = $this->container->get('http_kernel');
      70

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      $response = $httpKernel->forward('AcmeHelloBundle:Hello:fancy', array(
      'name' => $name,
      'color' => 'green',
      ));

      Ðåíäåðèíã Øàáëîíîâ

      Õîòÿ ýòî è íå ÿâëÿåòñÿ òðåáîâàíèåì, áîëüøèíñòâî êîíòðîëëåðîâ â êîíöå êîíöîâ áóäóò
      îòîáðàæàòü (ðåíäåðèòü) øàáëîí, êîòîðûé îòâå÷àåò çà ãåíåðàöèþ HTML (èëè äàííûõ â
      äðóãîì ôîðìàòå) äëÿ êîíòðîëëåðà. Ìåòîä renderView() ðåíäåðèò øàáëîí è âîçâðàùàåò
      åãî ñîäåðæèìîå. Êîíòåíò èç øàáëîíà ìîæåò áûòü èñïîëüçîâàí äëÿ ñîçäàíèÿ îáúåêòà
      Response:
      $content = $this->renderView('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));
      return new Response($content);

      Ýòè îïåðàöèè ìîãóò áûòü âûïîëíåíû çà îäèí øàã ïðè ïîìîùè ìåòîäà render(), êîòîðûé âîçâðàùàåò îáúåêò Response, ñîäåðæàùèé êîíòåíò øàáëîíà:
       îáîèõ ñëó÷àÿõ, áóäåò îòîáðàæåí øàáëîí Resources/views/Hello/index.html.twig
      èç ïàêåòà AcmeHelloBundle.
      Øàáëîíèçàòîð Symfony áîëåå ïîäðîáíî ðàññìàòðèâàåòñÿ â ãëàâå î

      Øàáëîíàõ

      Ñîâåò: Ìåòîä renderView - ýòî ïî ñóòè ÿðëûê äëÿ áûñòðîãî èñïîëüçîâàíèÿ øàáëîíèçàòîðà. Øàáëîíèçàòîð òàêæå ìîæíî èñïîëüçîâàòü íàïðÿìóþ:

      $templating = $this->get('templating');
      $content = $templating->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));

      Äîñòóï ê ñåðâèñàì

      Ïðè íàñëåäîâàíèè îò áàçîâîãî êîíòðîëëåðà, âû ìîæåòå ïîëó÷èòü äîñòóï ê ëþáîìó ñåðâèñó Symfony2 ïðè ïîìîùè ìåòîäà get(). Íèæå ïðåäñòàâëåíû îñíîâíûå ñåðâèñû, êîòîðûå âàì ìîãóò áûòü ïîëåçíû:
      $request = $this->getRequest();
      $templating = $this->get('templating');

      2.5.

      Êîíòðîëëåð

      71

      Symfony Documentation, Âûïóñê 2.0

      $router = $this->get('router');
      $mailer = $this->get('mailer');

       Symfony2 ïî óìîë÷àíèþ îïðåäåëåíà êó÷à ñåðâèñîâ è âû âîëüíû îïðåäåëèòü åù¼ ñòîëüêî æå ñîáñòâåííûõ. Äëÿ òîãî ÷òîáû îòîáðàçèòü ñïèñîê äîñòóïíûõ ñåðâèñîâ, èñïîëüçóéòå
      êîíñîëüíóþ êîìàíäó container:debug:
      php app/console container:debug

      Áîëüøå äàííûõ î ñåðâèñàõ âû ìîæåòå ïî÷åðïíóòü èç ãëàâû

      Service container .

      2.5.6 Ðàçáèðàåìñÿ ñ îøèáêàìè è 404 ñòðàíèöà

      Êîãäà ÷òî-ëèáî íå ìîæåò áûòü íàéäåíî, âû äîëæíû âåðíóòü ñòàòóñ-êîä 404. Äëÿ òîãî
      ÷òîáû ýòî ñäåëàòü, âû ìîæåòå ñãåíåðèðîâàòü îñîáûé òèï èñêëþ÷åíèÿ. Åñëè âû óíàñëåäîâàëè êîíòðîëëåð îò áàçîâîãî, âûïîëíèòå ñëåäóþùåå:
      public function indexAction()
      {
      $product = // òóò ïîëó÷àåì îáúåêò èç áàçû äàííûõ
      if (!$product) {
      throw $this->createNotFoundException('Ïðîäóêò íå ñóùåñòâóåò');
      }
      }

      return $this->render(...);

      Ìåòîä createNotFoundException() ñîçäà¼ò îñîáûé îáúåêò NotFoundHttpException, êîòîðûé â êîíå÷íîì èòîãå ïðîâîöèðóåò âîçâðàò HTTP 404 âíóòðè Symfony.
      Êîíå÷íî, âû âîëüíû âûçûâàòü ëþáóþ èñêëþ÷èòåëüíóþ ñèòóàöèþ â âàøåì êîíòðîëëåðå
      - Symfony2 àâòîìàòè÷åñêè âåðí¼ò HTTP ñòàòóñ-êîä 500.
      throw new \Exception('×òî-òî ïîøëî íå òàê!');

       ëþáîì ñëó÷àå, ïîëüçîâàòåëü óâèäèò ñòðàíèöó ñ òîé èëè èíîé îøèáêîé, à ðàçðàáîò÷èêó (ïðè èñïîëüçîâàíèè dev-îêðóæåíèÿ) áóäåò ïîêàçàíà ñòðàíèöà ñ ïîëíîé îòëàäî÷íîé
      èíôîðìàöèåé. Ýòè ñòðàíèöû îøèáîê ìîãóò áûòü èçìåíåíû. Áîëåå ïîäðîáíî îá ýòîì
      íàïèñàíî â êíèãå ðåöåïòîâ:  Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê .
      2.5.7 Ðàáîòà ñ Ñåññèÿìè

      Symfony2 ïðåäîñòàâëÿåò âàì îáúåêò, äëÿ ðàáîòû ñ ñåññèÿìè, êîòîðûé âû ìîæåòå èñïîëüçîâàòü äëÿ õðàíåíèÿ èíôîðìàöèè î ïîëüçîâàòåëå (åñëè îí ðåàëüíûé ÷åëîâåê, àâòîìà72

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      òè÷åñêèé áîò èëè æå âåá-ñåðâèñ) ìåæäó çàïðîñàìè. Ïî óìîë÷àíèþ, Symfony2 ñîõðàíÿåò
      àòðèáóòû â êóêàõ (cookie), èñïîëüçóÿ íàòèâíûå ñåññèè PHP.
      Ñîõðàíåíèå è ïîëó÷åíèå èíôîðìàöèè èç ñåññèè ìîæíî èñïîëüçîâàòü èç ëþáîãî êîíòðîëëåðà:
      $session = $this->getRequest()->getSession();
      // store an attribute for reuse during a later user request
      $session->set('foo', 'bar');
      // in another controller for another request
      $foo = $session->get('foo');
      // set the user locale
      $session->setLocale('fr');

      Ýòè àòðèáóòû áóäóò ñîîòâåòñòâîâàòü êîíêðåòíîìó ïîëüçîâàòåëþ, ïîêà ñóùåñòâóåò åãî
      ñåññèÿ.
      Flash-ñîîáùåíèÿ

      Âû òàêæå ìîæåòå ñîõðàíÿòü íåáîëüøèå ñîîáùåíèÿ, êîòîðûå ñîõðàíÿþòñÿ â ïîëüçîâàòåëüñêîé ñåññèè ìåæäó äâóìÿ çàïðîñàìè. Ýòè ñîîáùåíèÿ óäîáíî èñïîëüçîâàòü ïðè
      îáðàáîòêå ôîðì: âû õîòèòå âûïîëíèòü ïåðåíàïðàâëåíèå è îòîáðàçèòü îñîáîå ñîîáùåíèå
      ïðè ñëåäóþùåì çàïðîñå. Òàêèå ñîîáùåíèÿ íàçûâàþòñÿ ash-ñîîáùåíèÿìè.
      Íàïðèìåð, ïðåäñòàâüòå, ÷òî âû îáðàáàòûâàåòå îòïðàâêó ôîðìû:
      public function updateAction()
      {
      $form = $this->createForm(...);
      $form->bindRequest($this->getRequest());
      if ($form->isValid()) {
      // do some sort of processing
      $this->get('session')->setFlash('notice', 'Your changes were saved!');
      }
      }

      return $this->redirect($this->generateUrl(...));

      return $this->render(...);

      Ïîñëå îáðàáîòêè çàïðîñà êîíòðîëëåð óñòàíàâëèâàåò ash-ñîîáùåíèå notice è âûïîëíÿåò ïåðåíàïðàâëåíèå. Èìÿ (notice) íå óñòàíàâëèâàåòñÿ æ¼ñòêî - ýòî ëèøü îáîçíà÷åíèå
      2.5.

      Êîíòðîëëåð

      73

      Symfony Documentation, Âûïóñê 2.0

      òèïà ñîîáùåíèÿ.
      Â øàáëîíå ñëåäóþùåãî äåéñòâèÿ âû ìîæåòå èñïîëüçîâàòü ñëåäóþùèé êîä äëÿ îòîáðàæåíèÿ ñîîáùåíèÿ notice:
      ˆ

      Twig

      {% if app.session.hasFlash('notice') %}

      {{ app.session.flash('notice') }}

      {% endif %}

      ˆ

      PHP

      hasFlash('notice')): ?>

      getFlash('notice') ?>



      Ïî óìîë÷àíèþ, ash-ñîîáùåíèÿ äîëæíû æèòü ðîâíî îäèí çàïðîñ. Îíè ðàçðàáîòàíû
      èìåííî äëÿ òîãî, ÷òîáû èñïîëüçîâàòüñÿ âî âðåìÿ ïåðåíàïðàâëåíèÿìè òàê êàê ïîêàçàíî
      â ýòîì ïðèìåðå.
      2.5.8 Îáúåêò Îòâåòà

      Ê êîíòðîëëåðó ïðåäúÿâëÿåòñÿ ëèøü îäíî òðåáîâàíèå - âåðíóòü îáúåêò Response. Êëàññ
      Symfony\Component\HttpFoundation\Response ïðåäñòàâëÿåò ñîáîé PHP-àáñòðàêöèþ
      HTTP-îòâåòà - òåêñòîâîãî ñîîáùåíèÿ, ñîñòîÿùåãî èç HTTP-çàãîëîâêîâ è êîíòåíòà, êîòîðûé âîçâðàùàåòñÿ êëèåíòó:
      // ñîçäà¼òñÿ ïðîñòîé îáúåêò Response ñî ñòàòóñ-êîäîì 200 (ïî óìîë÷àíèþ)
      $response = new Response('Hello '.$name, 200);
      // ñîçäà¼òñÿ JSON-îòâåò ñî ñòàòóñ-êîäîì 2000
      $response = new Response(json_encode(array('name' => $name)));
      $response->headers->set('Content-Type', 'application/json');

      Ñîâåò: headers - ýòî îáúåêò Symfony\Component\HttpFoundation\HeaderBag, ñîäåð-

      æàùèé ìåòîäû äëÿ ÷òåíèÿ è èçìåíåíèÿ çàãîëîâêîâ îòâåòà Response. Èìåíà çàãîëîâêîâ
      íîðìàëèçîâàíû, òàê ÷òî Content-Type, content-type è äàæå content_type ýêâèâàëåíòíû.

      74

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      2.5.9 Îáúåêò çàïðîñà

      Ïîìèìî çíà÷åíèé çàïîëíèòåëåé èç ìàðøðóòà, êîíòðîëëåð òàêæå èìååò äîñòóï ê îáúåêòó
      Request, êîãäà îí ÿâëÿåòñÿ íàñëåäíèêîì áàçîâîãî êëàññà Controller:
      $request = $this->getRequest();
      $request->isXmlHttpRequest(); // is it an Ajax request?
      $request->getPreferredLanguage(array('en', 'fr'));
      $request->query->get('page'); // get a $_GET parameter
      $request->request->get('page'); // get a $_POST parameter

      Ïîäîáíî îáúåêòó Response, çàãîëîâêè çàïðîñà õðàíÿòñÿ â îáúåêòå HeaderBag è òàêæå
      ëåãêî äîñòóïíû.
      2.5.10 Çàêëþ÷åíèå

      Êîãäà âû ñîçäà¼òå ñòðàíèöó, â êîíå÷íîì èòîãå äîëæíû íàïèñàòü êîä, êîòîðûé ñîäåðæèò
      ëîãèêó ýòîé ñòðàíèöû. Â Symfony ýòà ëîãèêà íàçûâàåòñÿ êîíòðîëëåðîì, è ïðåäñòàâëÿåò ñîáîé PHP-ôóíêöèþ, êîòîðàÿ âûïîëíÿåò âñå íåîáõîäèìûå äåéñòâèÿ äëÿ òîãî ÷òîáû
      âåðíóòü îáúåêò Response, êîòîðûé áóäåò îòïðàâëåí ïîëüçîâàòåëþ.
      Äëÿ òîãî, ÷òîáû ñäåëàòü æèçíü ëåã÷å, âû ìîæåòå îòíàñëåäîâàòüñÿ îò êëàññà
      Controller, êîòîðûé ñîäåðæèò ìåòîäû äëÿ òèïè÷íûõ çàäà÷, ðåøàåìûõ êîíòðîëëåðîì.
      Íàïðèìåð, òàê êàê âû äîëæíû âåðíóòü HTML êîä - âû ìîæåòå èñïîëüçîâàòü ìåòîä
      render() è âåðíóòü êîíòåíò øàáëîíà.
      Â äðóãèõ ãëàâàõ âû óçíàåòå êàê êîíòðîëëåð ìîæåò áûòü èñïîëüçîâàí äëÿ ñîõðàíåíèÿ è
      ïîëó÷åíèÿ îáúåêòîâ èç áàçû äàííûõ, îáðàáàòûâàòü îòïðàâêó ôîðì, ðàáîòàòü ñ êýøåì
      è ìíîãîå äðóãîå.
      2.5.11 Äîïîëíèòåëüíî â êíèãå ðåöåïòîâ:

      ˆ

      Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê

      ˆ

      Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ

      2.6 Ìàðøðóòèçàöèÿ
      Êàæäîå ñåðü¼çíîå ïðèëîæåíèå äîëæíî îáÿçàòåëüíî èìåòü êðàñèâûå URL. Ýòî
      îçíà÷àåò, ÷òî ýòî ïðèëîæåíèå äîëæíî îñòàâèòü ïîçàäè ñòðàøíåíüêèå URL òèïà
      2.6.

      Ìàðøðóòèçàöèÿ

      75

      Symfony Documentation, Âûïóñê 2.0

      index.php?article_id=57 â ïîëüçó òàêèõ /read/intro-to-symfony.
      Îäíàêî ãèáêîñòü â ýòîì âîïðîñå - åù¼ áîëåå âàæíà, íåæåëè êðàñîòà. ×òî, åñëè âàì
      íóæíî èçìåíèòü URL /blog íà /news? Ñêîëüêî ññûëîê âàì ïðèä¼òñÿ îòûñêàòü è îáíîâèòü äëÿ ýòîãî? Åñëè æå âû èñïîëüçóåòå ìàðøðóòèçàòîð Symfony, ïîäîáíûå èçìåíåíèÿ
      äåëàòü ëåãêî.
      Ìàðøðóòèçàòîð Symfony2 ïîçâîëÿåò âàì îïðåäåëèòü êðåàòèâíûå URL, êîòîðûå âû ñìîæåòå ïðèâÿçàòü ê ðàçëè÷íûì îáëàñòÿì âàøåãî ïðèëîæåíèÿ. Ïî ïðî÷òåíèþ ýòîé ãëàâû
      âû ñìîæåòå äåëàòü ñëåäóþùåå:
      ˆ Ñîçäàâàòü ñëîæíûå ìàðøðóòû, ñîîòâåòñòâóþùèå êîíòðîëëåðàì;
      ˆ Ãåíåðèðîâàòü URL â øàáëîíàõ è êîíòðîëëåðàõ;
      ˆ Çàãðóçèòü ðåñóðñû äëÿ ìàðøðóòèçàöèè èç ïàêåòîâ (èëè èç ëþáûõ äðóãèõ èñòî÷íèêîâ);
      ˆ Îòëàæèâàòü ìàðøðóòû.
      2.6.1 Ìàðøðóòèçàöèÿ â äåéñòâèè

      ïî ñóòè ýòî ñâÿçóþùåå çâåíî ìåæäó øàáëîíîì URL è êîíòðîëëåðîì.
      Íàïðèìåð, ïðåäïîëîæèì, âàì íóæíî èñêàòü URL ïîõîæèå íà /blog/my-post èëè
      /blog/all-about-symfony è îòïðàâëÿòü èõ íà îáðàáîòêó â êîíòðîëëåð, êîòîðûé íàéä¼ò
      è îòîáðàçèò ýòè çàïèñè áëîãà. Ñîîòâåòñòâóþùèé ýòîé çàäà÷å ìàðøðóò - ïðîñò:
      Ìàðøðóò

      ˆ

      YAML

      # app/config/routing.yml
      blog_show:
      pattern: /blog/{slug}
      defaults: { _controller: AcmeBlogBundle:Blog:show }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:show



      ˆ

      76

      PHP

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      // app/config/routing.php
      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog_show', new Route('/blog/{slug}', array(
      '_controller' => 'AcmeBlogBundle:Blog:show',
      )));
      return $collection;

      Øàáëîí, îïðåäåëÿåìûé ìàðøðóòîì blog_show ðàáîòàåò êàê âûðàæåíèå /blog/*, ãäå
      ìåòàñèìâîëîì ÿâëÿåòñÿ èìÿ slug. Äëÿ URL /blog/my-blog-post ïåðåìåííàÿ slug ïîëó÷àåò çíà÷åíèå my-blog-post, êîòîðîå áóäåò äîñòóïíî äëÿ èñïîëüçîâàíèÿ â êîíòðîëëåðå.
      Ïàðàìåòð _controller - ýòî ñëóæåáíûé êëþ÷, êîòîðûé ñîîáùàåò Symfony, êàêîé
      èìåííî êîíòðîëëåð äîëæåí áûòü âûïîëíåí, êîãäà ìàðøðóò ñîâïàäàåò ñ URL. Ñòðîêà
      _controller, íàçûâàåòñÿ ëîãè÷åñêèì èìåíåì . Ëîãè÷åñêîå èìÿ óêàçûâàåò íà íåêîòîðûé
      ÐHP-êëàññ è åãî ìåòîä:
      // src/Acme/BlogBundle/Controller/BlogController.php
      namespace Acme\BlogBundle\Controller;
      use Symfony\Bundle\FrameworkBundle\Controller\Controller;
      class BlogController extends Controller
      {
      public function showAction($slug)
      {
      $blog = // èñïîëüçóéòå ïåðåìåííóþ $slug, äëÿ òîãî ÷òîáû ïîëó÷èòü çàïèñü èç áàçû äàííûõ

      }

      }

      return $this->render('AcmeBlogBundle:Blog:show.html.twig', array(
      'blog' => $blog,
      ));

      Ïîçäðàâëÿåì! Âû òîëüêî ÷òî ñîçäàëè âàø ïåðâûé ìàðøðóò è ñâÿçàëè åãî ñ êîíòðîëëåðîì. Òåïåðü, êîãäà âû ïîñåòèòå ñòðàíèöó /blog/my-post, áóäåò âûïîëíåí êîíòðîëëåð
      showAction è ïåðåìåííàÿ $slug áóäåò ðàâíà my-post.
      Ýòî è åñòü öåëü ìàðøðóòèçàòîðà Symfony2: óñòàíàâëèâàòü ñîîòâåòñòâèå ìåæäó URL
      çàïðîñà è êîíòðîëëåðîì. Äàëåå â ýòîé ãëàâå âû óçíàåòå âñå âîçìîæíûå òðþêè, êîòîðûå
      ïîçâîëÿþò ëåãêî ïèñàòü ìàðøðóòû äàæå äëÿ ñëîæíûõ URL.

      2.6.

      Ìàðøðóòèçàöèÿ

      77

      Symfony Documentation, Âûïóñê 2.0

      2.6.2 Ìàðøðóòèçàöèÿ; ×òî ïîä êàïîòîì

      Êîãäà ñîçäà¼òñÿ çàïðîñ ê âàøåìó ïðèëîæåíèþ, îí ñîäåðæèò òî÷íûé àäðåñ ðåñóðñà,
      êîòîðûé çàïðàøèâàåòñÿ êëèåíòîì. Ýòîò àäðåñ íàçûâàåòñÿ URL (èëè URI) è ìîæåò
      âûãëÿäåòü ñëåäóþùèì îáðàçîì: /contact, /blog/read-me èëè åù¼ êàêèì-òî ïîõîæèì
      îáðàçîì. Äàâàéòå ðàññìîòðèì ñëåäóþùèé HTTP-çàïðîñ â êà÷åñòâå ïðèìåðà:
      GET /blog/my-blog-post

      Öåëü ñèñòåìû ìàðøðóòèçàöèè Symfony2 - ðàçáîð ýòîãî URL è îïðåäåëåíèå òîãî, êàêîé
      êîíòðîëëåð äîëæåí áûòü âûïîëíåí. Ïðîöåññ öåëèêîì âûãëÿäèò òàê:
      1. Çàïðîñ îáðàáàòûâàåòñÿ ôðîíò-êîíòðîëëåðîì Symfony2 (íàïðèìåð app.php);
      2. ßäðî Symfony2 (Kernel), âûçûâàåò ìàðøðóòèçàòîð äëÿ àíàëèçà çàïðîñà;
      3. Ìàðøðóòèçàòîð óñòàíàâëèâàåò ñîîòâåòñòâèå ìåæäó âõîäÿùèì URL è íåêîòîðûì
      ìàðøðóòîì è âîçâðàùàåò èíôîðìàöèþ î ìàðøðóòå, âêëþ÷àÿ äàííûå î òîì, êàêîé
      êîíòðîëëåð òðåáóåòñÿ âûïîëíèòü;
      4. ßäðî Symfony2 âûïîëíÿåò êîíòðîëëåð, êîòîðûé â êîíå÷íîì èòîãå âîçâðàùàåò îáúåêò Response.

      Ðèñ. 2.2: Ñëîé ìàðøðóòèçàöèè - ýòî èíñòðóìåíò, êîòîðûé òðàíñëèðóåò âõîäÿùèé URL
      â êîíòðîëëåð, êîòîðûé íóæíî âûïîëíèòü äëÿ åãî îáðàáîòêè.

      2.6.3 Ñîçäàíèå ìàðøðóòîâ

      Symfony çàãðóæàåò âñå ìàðøðóòû, îïðåäåë¼ííûå äëÿ âàøåãî ïðèëîæåíèÿ, èç îäíîãî
      ôàéëà íàñòðîåê. Êàê ïðàâèëî, ýòîò ôàéë íàçûâàåòñÿ app/config/routing.yml, íî ïðè
      æåëàíèè íàèìåíîâàíèå ôàéëà êîíôèãóðàöèè ìîæíî èçìåíèòü íà äðóãîå (â òîì ÷èñëå
      íà ôàéë ôîðìàòà XML èëè PHP) â êîíôèãóðàöèîííîì ôàéëå ïðèëîæåíèÿ:
      78

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      YAML

      # app/config/config.yml
      framework:
      # ...
      router:
      { resource: "%kernel.root_dir%/config/routing.yml" }

      ˆ

      XML







      ˆ

      PHP

      // app/config/config.php
      $container->loadFromExtension('framework', array(
      // ...
      'router'
      => array('resource' => '%kernel.root_dir%/config/routing.php'),
      ));

      Ñîâåò: Íå ñìîòðÿ íà òî, ÷òî âñå ìàðøðóòû çàãðóæàþòñÿ èç îäíîãî ôàéëà, îáû÷íîé
      ïðàêòèêîé ÿâëÿåòñÿ ïîäêëþ÷åíèå äîïîëíèòåëüíûõ ðåñóðñîâ âíóòðè ýòîãî ôàéëà (ñì.
      ñåêöèþ Ïîäêëþ÷åíèå âíåøíèõ ðåñóðñîâ äëÿ ìàðøðóòèçàöèè ).

      Áàçîâàÿ íàñòðîéêà ìàðøðóòà

      Îïðåäåëèòü íîâûé ìàðøðóò íå ñëîæíî, òèïè÷íîå ïðèëîæåíèå áóäåò èìåòü ìíîãî ðàçëè÷íûõ ìàðøðóòîâ. Ñàìûé ïðîñòîé ìàðøðóò ñîñòîèò èç äâóõ ÷àñòåé: øàáëîíà URL
      (pattern) è ìàññèâà defaults:
      ˆ

      YAML

      _welcome:
      pattern: /
      defaults: { _controller: AcmeDemoBundle:Main:homepage }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing
      2.6.

      Ìàðøðóòèçàöèÿ

      79

      Symfony Documentation, Âûïóñê 2.0


      AcmeDemoBundle:Main:homepage



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('_welcome', new Route('/', array(
      '_controller' => 'AcmeDemoBundle:Main:homepage',
      )));
      return $collection;

      Ýòîò ìàðøðóò ñîîòâåòñòâóåò ãëàâíîé ñòðàíèöå (/) è ñòàâèò åé â ñîîòâåòñòâèå êîíòðîëëåð AcmeDemoBundle:Main:homepage. Symfony2 ïåðåâîäèò ñòðîêó _controller â èìÿ
      ôóíêöèè, êîòîðóþ íåîáõîäèìî âûïîëíèòü. Ýòîò ïðîöåññ áóäåò îáúÿñíÿòüñÿ â ñåêöèè
      Øàáëîí Èìåíîâàíèÿ Êîíòðîëëåðà .
      Ìàðøðóòèçàöèÿ è Çàïîëíèòåëè (Placeholders)

      Êîíå÷íî æå ñèñòåìà ìàðøðóòèçàöèè ïîääåðæèâàåò è áîëåå èíòåðåñíûå ìàðøðóòû.
      Ìíîãèå ìàðøðóòû áóäóò ñîäåðæàòü îäèí èëè áîëåå çàïîëíèòåëåé (placeholders):
      ˆ

      YAML

      blog_show:
      pattern: /blog/{slug}
      defaults: { _controller: AcmeBlogBundle:Blog:show }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:show


      80

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog_show', new Route('/blog/{slug}', array(
      '_controller' => 'AcmeBlogBundle:Blog:show',
      )));
      return $collection;

      Øàáëîí áóäåò ñîîòâåòñòâîâàòü ëþáîìó URL ïîõîæåìó íà /blog/*. ×òî åù¼ áîëåå âàæíî
      - çíà÷åíèå, ñîîòâåòñòâóþùåå çàïîëíèòåëþ {slug}, áóäåò äîñòóïíî â âàøåì êîíòðîëëåðå.
      Äðóãèìè ñëîâàìè, åñëè äàí URL /blog/hello-world, ïåðåìåííàÿ $slug ñî çíà÷åíèåì
      hello-world áóäåò äîñòóïíà â êîíòðîëëåðå. Ýòó âîçìîæíîñòü ìîæíî èñïîëüçîâàòü, íàïðèìåð, äëÿ çàãðóçêè çàïèñè áëîãà, ñîîòâåòñòâóþùåé ýòîé ñòðîêå.
      Òåì íå ìåíåå, ýòîò øàáëîí íå áóäåò ñîîòâåòñòâîâàòü URL /blog. Ýòî âûçâàíî òåì
      ôàêòîì, ÷òî çàïîëíèòåëü ïî óìîë÷àíèþ ÿâëÿåòñÿ îáÿçàòåëüíûì ïàðàìåòðîì. Îäíàêî,
      åñëè äîáàâèòü çàïîëíèòåëþ çíà÷åíèå ïî óìîë÷àíèþ â ìàññèâ defaults.
      Îáÿçàòåëüíûå è Îïöèîíàëüíûå Çàïîëíèòåëè

      Äëÿ òîãî, ÷òîáû ðàçíîîáðàçèòü ïðîöåññ, äàâàéòå ñîçäàäèì íîâûé ìàðøðóò, êîòîðûé
      îòîáðàæàåò ñïèñîê âñåõ çàïèñåé â áëîãå äëÿ íàøåãî âîîáðàæàåìîãî ïðèëîæåíèÿ:
      ˆ

      YAML

      blog:
      pattern: /blog
      defaults: { _controller: AcmeBlogBundle:Blog:index }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:index



      ˆ
      2.6.

      PHP

      Ìàðøðóòèçàöèÿ

      81

      Symfony Documentation, Âûïóñê 2.0

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog', new Route('/blog', array(
      '_controller' => 'AcmeBlogBundle:Blog:index',
      )));
      return $collection;

      Ïîêà ÷òî ýòîò ìàðøðóò âûãëÿäèò ïðîùå ïðîñòîãî - îí íå ñîäåðæèò çàïîëíèòåëåé è ñîîòâåòñòâóåò ëèøü îäíîìó URL /blog. Íó à åñëè âàì ïîòðåáóåòñÿ, ÷òîáû äàííûé ìàðøðóò
      ïîääåðæèâàë ïîñòðàíè÷íóþ íàâèãàöèþ è URL /blog/2 îòîáðàæàë âòîðóþ ñòðàíèöó ñ
      çàïèñÿìè áëîãà? Äîáàâèì ê ìàðøðóòó çàïîëíèòåëü {page}:
      ˆ

      YAML

      blog:
      pattern: /blog/{page}
      defaults: { _controller: AcmeBlogBundle:Blog:index }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:index



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog', new Route('/blog/{page}', array(
      '_controller' => 'AcmeBlogBundle:Blog:index',
      )));
      return $collection;

      82

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Ïîäîáíî çàïîëíèòåëþ {slug}, çíà÷åíèå ñîîòâåòñòâóþùåå {page} áóäåò äîñòóïíî âíóòðè
      êîíòðîëëåðà. Ýòî çíà÷åíèå ìîæåò áûòü èñïîëüçîâàíî äëÿ òîãî, ÷òîáû îïðåäåëèòü, êàêîé
      íàáîð çàïèñåé áëîãà îòîáðàçèòü äëÿ äàííîé ñòðàíèöû.
      Íî ïîãîäèòå-êà! Òàê êàê çàïîëíèòåëü ïî óìîë÷àíèþ îáÿçàòåëåí, ìàðøðóò òåïåðü íå ñìîæåò ñîîòâåòñòâîâàòü ïðîñòî /blog. Âìåñòî ýòîãî, åñëè âû çàõîòèòå îòîáðàçèòü ïåðâóþ
      ñòðàíèöó, âàì íóæíî áóäåò èñïîëüçîâàòü URL /blog/1! Ïîñêîëüêó ýòî ñîâåðøåííî
      íåïðèåìëåìî, ïîòðåáóåòñÿ èçìåíèòü ïàðàìåòð {page} è ñäåëàòü åãî îïöèîíàëüíûì. Ñäåëàòü ýòî ìîæíî, âêëþ÷èâ åãî â ìàññèâ defaults:
      ˆ

      YAML

      blog:
      pattern: /blog/{page}
      defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:index
      1



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog', new Route('/blog/{page}', array(
      '_controller' => 'AcmeBlogBundle:Blog:index',
      'page' => 1,
      )));
      return $collection;

      Äîáàâèâ page â ìàññèâ defaults, âû ñäåëàëè çàïîëíèòåëü {page} íåîáÿçàòåëüíûì. URL
      /blog áóäåò ñîîòâåòñòâîâàòü ìàðøðóòó è çíà÷åíèå ïàðàìåòðà page áóäåò ðàâíî 1. URL
      /blog/2 òàêæå áóäåò ñîîòâåòñòâîâàòü ýòîìó ìàðøðóòó, ïðèñâàèâàÿ ïàðàìåòðó page çíà÷åíèå 2. Îòëè÷íî.

      2.6.

      Ìàðøðóòèçàöèÿ

      83

      Symfony Documentation, Âûïóñê 2.0

      /blog
      /blog/1
      /blog/2

      {page} = 1
      {page} = 1
      {page} = 2

      Äîáàâëÿåì Îãðàíè÷åíèÿ

      Äàâàéòå âçãëÿíåì íà ìàðøðóòû, êîòîðûå ìû äîáàâèëè ðàíåå:
      ˆ

      YAML

      blog:
      pattern: /blog/{page}
      defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
      blog_show:
      pattern: /blog/{slug}
      defaults: { _controller: AcmeBlogBundle:Blog:show }

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:index
      1


      AcmeBlogBundle:Blog:show



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog', new Route('/blog/{page}', array(
      '_controller' => 'AcmeBlogBundle:Blog:index',
      'page' => 1,
      )));

      84

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      $collection->add('blog_show', new Route('/blog/{show}', array(
      '_controller' => 'AcmeBlogBundle:Blog:show',
      )));
      return $collection;

      Ìîæåòå îïðåäåëèòü òóò ïðîáëåìó? Îáðàòèòå âíèìàíèå, ÷òî îáà ìàðøðóòà èìåþò ïîõîæèå øàáëîíû è ñîîòâåòñòâóþò URL âèäà /blog/*. Ìàðøðóòèçàòîð Symfony âñåãäà
      áóäåò âûáèðàòü ïåðâûé ñîâïàâøèé ìàðøðóò, êîòîðûé îí íàéä¼ò. Äðóãèìè ñëîâàìè,
      ìàðøðóò blog_show íèêîãäà íå ñîâïàä¼ò è íå áóäåò âûçâàí ñîîòâåòñòâóþùèé êîíòðîëëåð. Âìåñòî ýòîãî URL âèäà /blog/my-blog-post áóäåò ñîîòâåòñòâîâàòü ïåðâîìó ìàðøðóòó (blog) è âîçâðàùàòü áåññìûñëåííîå äëÿ ïàðàìåòðà {page} çíà÷åíèå my-blog-post.

      URL

      /blog/2
      /blog/my-blog-post

      route

      blog
      blog

      parameters

      {page} = 2
      {page} = my-blog-post

      Ðåøåíèåì ýòîé ïðîáëåìû ÿâëÿåòñÿ äîáàâëåíèå îãðàíè÷åíèé â ìàðøðóò. Ìàðøðóòû â
      ýòîì ïðèìåðå áóäóò ðàáîòàòü, åñëè øàáëîí /blog/{page} áóäåò ñîîòâåòñòâîâàòü URL
      ëèøü â òîì ñëó÷àå, êîãäà {page} áóäåò öåëûì ÷èñëîì. Ê ñ÷àñòüþ, îãðàíè÷åíèÿ â âèäå
      ðåãóëÿðíûõ âûðàæåíèé ëåãêî ìîãóò áûòü äîáàâëåíû ê ëþáîìó ïàðàìåòðó. Íàïðèìåð:
      ˆ

      YAML

      blog:
      pattern: /blog/{page}
      defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
      requirements:
      page: \d+

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeBlogBundle:Blog:index
      1
      \d+



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      2.6.

      Ìàðøðóòèçàöèÿ

      85

      Symfony Documentation, Âûïóñê 2.0

      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('blog', new Route('/blog/{page}', array(
      '_controller' => 'AcmeBlogBundle:Blog:index',
      'page' => 1,
      ), array(
      'page' => '\d+',
      )));
      return $collection;

      Îãðàíè÷åíèå \d+ - ýòî ðåãóëÿðíîå âûðàæåíèå, êîòîðîå ñîîáùàåò ìàðøðóòèçàòîðó, ÷òî
      çíà÷åíèå ïàðàìåòðà {page} äîëæíî áûòü ÷èñëîâûì. Ìàðøðóò blog ïî-ïðåæíåìó áóäåò
      ñîîòâåòñòâîâàòü URL âèäà /blog/2 (òàê êàê 2 ýòî ÷èñëî), íî îí óæå íå áóäåò ñîîòâåòñòâîâàòü URL âèäà /blog/my-blog-post (òàê êàê my-blog-post íå ÿâëÿåòñÿ ÷èñëîì ).
      Â ðåçóëüòàòå URL /blog/my-blog-post áóäåò ñîîòâåòñòâîâàòü ìàðøðóòó blog_show.

      URL

      /blog/2
      /blog/my-blog-post

      route

      blog
      blog_show

      parameters

      {page} = 2
      {slug} = my-blog-post

      Áîëåå ðàííèé ìàðøðóò âñåãäà âûèãðûâàåò
      ×òî æå îçíà÷àåò òîò ôàêò, ÷òî ïîðÿäîê ìàðøðóòîâ î÷åíü âàæåí? Åñëè ìàðøðóò
      blog_show áóäåò ðàñïîëîæåí âûøå ìàðøðóòà blog, òî URL /blog/2 áóäåò ñîîòâåòñòâîâàòü ìàðøðóòó blog_show âìåñòî ìàðøðóòà blog òàê êàê ïàðàìåòð {slug} íå
      èìååò îãðàíè÷åíèé. Èñïîëüçóÿ ïðàâèëüíûé ïîðÿäîê è ðàçóìíûå îãðàíè÷åíèÿ âû
      ñìîæåòå ñäåëàòü âñ¼ ÷òî âàì óãîäíî.
      Òàê êàê îãðàíè÷åíèÿ äëÿ ïàðàìåòðîâ - ýòî ðåãóëÿðíûå âûðàæåíèÿ, ñëîæíîñòü è ãèáêîñòü êàæäîãî îãðàíè÷åíèÿ ëåæèò íà âàøåé ñîâåñòè. Ïðåäïîëîæèì, ÷òî ãëàâíàÿ ñòðàíèöà âàøåãî ïðèëîæåíèÿ äîñòóïíà íà äâóõ ðàçëè÷íûõ ÿçûêàõ, â çàâèñèìîñòè îò URL:
      ˆ

      YAML

      homepage:
      pattern: /{culture}
      defaults: { _controller: AcmeDemoBundle:Main:homepage, culture: en }
      requirements:
      culture: en|fr

      ˆ

      XML



      86

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeDemoBundle:Main:homepage
      en
      en|fr



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('homepage', new Route('/{culture}', array(
      '_controller' => 'AcmeDemoBundle:Main:homepage',
      'culture' => 'en',
      ), array(
      'culture' => 'en|fr',
      )));
      return $collection;

      Äëÿ âõîäÿùèõ çàïðîñîâ, ÷àñòü URL, ñîîòâåòñòâóþùàÿ {culture} äîëæíà óäîâëåòâîðÿòü ðåãóëÿðíîìó âûðàæåíèþ (en|fr):
      /
      /en
      /fr
      /es

      {culture} = en
      {culture} = en
      {culture} = fr
      íå ñîîòâåòñòâóåò ìàðøðóòó

      Äîáàâëÿåì Îãðàíè÷åíèÿ äëÿ HTTP-ìåòîäà

      Â äîïîëíåíèå ê URL, âû òàêæå ìîæåòå ïðîâåðÿòü HTTP-ìåòîä âõîäÿùåãî çàïðîñà
      (GET, HEAD, POST, PUT, DELETE). Ïðåäïîëîæèì ó âàñ åñòü ôîðìà êîíòàêòîâ ñ
      äâóìÿ êîíòðîëëåðàìè - îäèí äëÿ îòîáðàæåíèÿ ôîðìû (GET çàïðîñ) è äðóãîé - äëÿ
      îáðàáîòêè ôîðìû, êîãäà îíà îòïðàâëåíà ïîëüçîâàòåëåì (POST çàïðîñ). Îãðàíè÷åíèÿ
      äëÿ HTTP-ìåòîäà ìîæíî çàäàòü ñëåäóþùèì îáðàçîì:
      ˆ

      YAML

      contact:
      pattern: /contact
      defaults: { _controller: AcmeDemoBundle:Main:contact }
      2.6.

      Ìàðøðóòèçàöèÿ

      87

      Symfony Documentation, Âûïóñê 2.0

      requirements:
      _method: GET
      contact_process:
      pattern: /contact
      defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
      requirements:
      _method: POST

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeDemoBundle:Main:contact
      GET


      AcmeDemoBundle:Main:contactProcess
      POST



      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('contact', new Route('/contact', array(
      '_controller' => 'AcmeDemoBundle:Main:contact',
      ), array(
      '_method' => 'GET',
      )));
      $collection->add('contact_process', new Route('/contact', array(
      '_controller' => 'AcmeDemoBundle:Main:contactProcess',
      ), array(
      '_method' => 'POST',
      )));
      return $collection;
      88

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Ïðåíåáðåãàÿ òåì, ÷òî îáà ïðåäñòàâëåííûõ âûøå ìàðøðóòà èìåþò èäåíòè÷íûå øàáëîíû
      (/contact), ïåðâûé ìàðøðóò áóäåò ñîîòâåòñòâîâàòü òîëüêî GET-çàïðîñàì, à âòîðîé,
      â ñâîþ î÷åðåäü, áóäåò ñîîòâåòñòâîâàòü òîëüêî POST-çàïðîñàì. Ýòî îçíà÷àåò, ÷òî âû
      ñìîæåòå îòîáðàæàòü è îòïðàâëÿòü ôîðìó, èñïîëüçóÿ îäèí è òîò æå URL è èñïîëüçîâàòü
      ðàçëè÷íûå êîíòðîëëåðû äëÿ êàæäîãî èç ýòèõ äåéñòâèé.

      Ïðèìå÷àíèå: Åñëè îãðàíè÷åíèÿ íà _method íå óêàçàíû, ìàðøðóò áóäåò ñîîòâåòñòâîâàòü

      ëþáîìó

      ìåòîäó.

      Êàê è ëþáûå äðóãèå îãðàíè÷åíèÿ, îãðàíè÷åíèÿ äëÿ _method îáðàáàòûâàþòñÿ êàê ðåãóëÿðíûå âûðàæåíèÿ. Äëÿ òîãî, ÷òîáû ñîîòâåòñòâîâàòü êàê GET òàê è POST çàïðîñàì, âû
      ìîæåòå èñïîëüçîâàòü îãðàíè÷åíèå GET|POST.
      Ïðîäâèíóòàÿ Ìàðøðóòèçàöèÿ â Ïðèìåðàõ

      Íà òåêóùèé ìîìåíò, âû èìååòå âñþ íåîáõîäèìóþ èíôîðìàöèþ, äëÿ ñîçäàíèÿ ñëîæíûõ
      ñòðóêòóð ìàðøðóòèçàöèè â Symfony. Íèæå ìû ïîêàæåì âàì, íàñêîëüêî ãèáêîé ìîæåò
      áûòü ñèñòåìà ìàðøðóòèçàöèè:
      ˆ

      YAML

      article_show:
      pattern: /articles/{culture}/{year}/{title}.{_format}
      defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
      requirements:
      culture: en|fr
      _format: html|rss
      year:
      \d+

      ˆ

      XML



      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeDemoBundle:Article:show
      html
      en|fr
      html|rss
      \d+



      2.6.

      Ìàðøðóòèçàöèÿ

      89

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      PHP

      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;

      $collection = new RouteCollection();
      $collection->add('homepage', new Route('/articles/{culture}/{year}/{title}.{_format}', arra
      '_controller' => 'AcmeDemoBundle:Article:show',
      '_format' => 'html',
      ), array(
      'culture' => 'en|fr',
      '_format' => 'html|rss',
      'year' => '\d+',
      )));
      return $collection;

      Êàê âû ìîæåòå âèäåòü, ýòîò ìàðøðóò ñðàáîòàåò ëèøü â òîì ñëó÷àå, åñëè {culture} â
      URL áóäåò ëèáî en ëèáî fr è {year} áóäåò ÷èñëîì. Ýòîò ìàðøðóò òàêæå ïîêàçûâàåò,
      ÷òî âû ìîæåòå èñïîëüçîâàòü ïîìèìî ñëýøà (/) òî÷êó ìåæäó äâóìÿ çàïîëíèòåëÿìè.
      URL, ñîîòâåòñòâóþùèé ýòîìó ìàðøðóòó ìîæåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:
      ˆ /articles/en/2010/my-post
      ˆ /articles/fr/2010/my-post.rss

      Îñîáûé ïàðàìåòð ìàðøðóòà _format
      Ýòîò ïðèìåð òàêæå èñïîëüçóåò îñîáûé ïàðàìåòð ìàðøðóòà - _format. Ïðè èñïîëüçîâàíèè ýòîãî ïàðàìåòðà, ñîîòâåòñòâóþùåå çíà÷åíèå ñòàíîâèòñÿ ôîðìàòîì çàïðîñà â îáúåêòå Request.  êîíå÷íîì ñ÷¼òå, ôîðìàò çàïðîñà èñïîëüçóåòñÿ äëÿ òàêèõ
      äåéñòâèé, êàê óñòàíîâêà Content-Type äëÿ îòâåòà (íàïðèìåð ôîðìàò çàïðîñà json
      òðàíñôîðìèðóåòñÿ â Content-Type application/json). Ýòîò ïàðàìåòð òàêæå ìîæåò áûòü èñïîëüçîâàí â êîíòðîëëåðå äëÿ îòîáðàæåíèÿ ðàçëè÷íûõ øàáëîíîâ äëÿ
      êàæäîãî âîçìîæíîãî åãî çíà÷åíèÿ. Ïàðàìåòð _format ÿâëÿåòñÿ âåñüìà óäîáíûì
      ðåøåíèåì ïðè íåîáõîäèìîñòè îòîáðàæàòü îäèí è òîò æå êîíòåíò â ðàçëè÷íûõ ôîðìàòàõ.

      Ñïåöèàëüíûå ïàðàìåòðû ìàðøðóòà

      Êàê âû íàâåðíîå îáðàòèëè âíèìàíèå, êàæäûé ïàðàìåòð ìàðøðóòà èëè çíà÷åíèå ïî
      óìîë÷àíèþ â êîíå÷íîì èòîãå äîñòóïåí â âèäå àðãóìåíòà â ìåòîäå êîíòðîëëåðà.  äîïîëíåíèå ê ýòîìó åñòü òàêæå òðè ñïåöèàëüíûõ ïàðàìåòðà, êàæäûé èç êîòîðûõ äîáàâëÿåò
      óíèêàëüíûå âîçìîæíîñòè âíóòðè âàøåãî ïðèëîæåíèÿ:

      90

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ˆ _controller: Êàê âû óæå çíàåòå, ýòîò ïàðàìåòð èñïîëüçóåòñÿ äëÿ òîãî, ÷òîáû
      îïðåäåëèòü êàêîé êîíòðîëëåð áóäåò âûïîëíåí, êîãäà ìàðøðóò ñîâïàäàåò ñ URL;
      ˆ _format: Èñïîëüçóåòñÿ äëÿ îïðåäåëåíèÿ çàïðàøèâàåìîãî ôîðìàòà (ñì.
      ìàðøðóòà _format );

      ïàðàìåòð

      ˆ _locale: Èñïîëüçóåòñÿ äëÿ òîãî, ÷òîáû óñòàíîâèòü ëîêàëü â ñåññèè (ñì.
      URL);

      ëîêàëü â

      2.6.4 Øàáëîí Èìåíîâàíèÿ Êîíòðîëëåðà

      Êàæäûé ìàðøðóò äîëæåí èìåòü ïàðàìåòð _controller, êîòîðûé îïðåäåëÿåò, êàêîé
      èìåííî êîíòðîëëåð áóäåò âûïîëíåí, êîãäà ñîîòâåòñòâóþùèé ìàðøðóò ñîâïàä¼ò ñ URL.
      Ýòîò ïàðàìåòð èñïîëüçóåò ïðîñòîé ñòðîêîâûé øàáëîí, èìåíóåìûé ëîãè÷åñêèì èìåíåì
      êîíòðîëëåðà, êîòîðîìó Symfony ñòàâèò â ñîîòâåòñòâèå PHP ìåòîä è êëàññ. Øàáëîí
      ñîñòîèò èç òð¼õ ÷àñòåé, ðàçäåë¼ííûõ äâîåòî÷èåì:

      ïàêåò:êîíòðîëëåð:äåéñòâèå
      Íàïðèìåð, åñëè _controller èìååò çíà÷åíèå AcmeBlogBundle:Blog:show, òî ýòî îçíà÷àåò ñëåäóþùåå:

      Bundle

      AcmeBlogBundle

      Controller Class

      BlogController

      Method Name

      showAction

      Êîíòðîëëåð ìîæåò âûãëÿäåòü òàê:
      // src/Acme/BlogBundle/Controller/BlogController.php
      namespace Acme\BlogBundle\Controller;
      use Symfony\Bundle\FrameworkBundle\Controller\Controller;
      class BlogController extends Controller
      {
      public function showAction($slug)
      {
      // ...
      }
      }

      Îáðàòèòå âíèìàíèå, ÷òî Symfony äîáàâëÿåò ñòðîêó Controller ó èìåíè êëàññà (Blog
      => BlogController) è Action ê èìåíè ìåòîäà (show => showAction).
      Âû òàêæå ìîæåòå ññûëàòüñÿ íà ýòîò êëàññ, èñïîëüçóÿ ïîëíîå èìÿ êëàññà è ìåòîäà: Acme\BlogBundle\Controller\BlogController::showAction. Íî, åñëè âû ñëåäóåòå
      íåñêîëüêèì ïðîñòûì ñîãëàøåíèÿì, ëîãè÷åñêîå èìÿ áóäåò áîëåå óäîáíî.

      2.6.

      Ìàðøðóòèçàöèÿ

      91

      Symfony Documentation, Âûïóñê 2.0

      Ïðèìå÷àíèå:  äîïîëíåíèå ê èñïîëüçîâàíèþ ëîãè÷åñêîãî èìåíè è ïîëíîãî èìåíè

      êëàññà, Symfony ïîääåðæèâàåò òðåòèé òèï ññûëîê íà êîíòðîëëåð. Ýòîò ìåòîä èñïîëüçóåò îäíî äâîåòî÷èå â êà÷åñòâå ðàçäåëèòåëÿ (íàïðèìåð service_name:indexAction) è
      ññûëàåòñÿ íà êîòðîëëåð, îïðåäåë¼ííûé êàê ñåðâèñ (ñì. Êàê îïðåäåëÿòü Êîíòðîëëåðû
      â êà÷åñòâå ñåðâèñîâ ).

      2.6.5 Ïàðàìåòðû ìàðøðóòà è Àðãóìåíòû êîíòðîëëåðà

      Ïàðàìåòðû ìàðøðóòà (íàïðèìåð {slug}) î÷åíü âàæíû, òàê êàê êàæäûé ïàðàìåòð áóäåò
      äîñòóïåí â êà÷åñòâå àðãóìåíòà â ìåòîäå-êîíòðîëëåðå:
      public function showAction($slug)
      {
      // ...
      }

      Ôàêòè÷åñêè, âñå defaults îáúåäèíÿþòñÿ ñî çíà÷åíèÿìè ïàðàìåòðîâ è ôîðìèðóþò îäèí
      ìàññèâ. Êàæäûé êëþ÷ òàêîãî ìàññèâà äîñòóïåí â êà÷åñòâå àðãóìåíòà âíóòðè êîíòðîëëåðà.
      Äðóãèìè ñëîâàìè, äëÿ êàæäîãî àðãóìåíòà âàøåãî ìåòîäà-êîíòðîëëåðà, Symfony èùåò
      ïàðàìåòð ìàðøðóòà ñ òåì æå èìåíåì è ïðèñâàèâàåò åãî çíà÷åíèå ýòîìó àðãóìåíòó. Â
      ïðîäâèíóòîì ïðèìåðå ðàíåå ëþáàÿ êîìáèíàöèÿ (â ëþáîì ïîðÿäêå) ñëåäóþùèõ ïåðåìåííûõ ìîæåò áûòü èñïîëüçîâàíà â êà÷åñòâå àðãóìåíòîâ ìåòîäà showAction():
      ˆ $culture
      ˆ $year
      ˆ $title
      ˆ $_format
      ˆ $_controller
      Òàê êàê çàïîëíèòåëè è ìàññèâ defaults îáúåäèíÿþòñÿ, äàæå ïåðåìåííàÿ $_controller
      ñòàíîâèòñÿ äîñòóïíà. Áîëåå ïîäðîáíî ýòî îïèñàíî â ñåêöèè Ïàðàìåòðû ìàðøðóòà â
      êà÷åñòâå àðãóìåíòîâ Êîíòðîëëåðà .

      Ñîâåò: Âû òàêæå ìîæåòå èñïîëüçîâàòü ïåðåìåííóþ $_route, êîòîðàÿ ñîäåðæèò èìÿ
      ñîîòâåòñòâóþùåãî ìàðøðóòà.

      92

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      2.6.6 Ïîäêëþ÷åíèå âíåøíèõ ðåñóðñîâ äëÿ ìàðøðóòèçàöèè

      Âñå ìàðøðóòû çàãðóæàþòñÿ ïîñðåäñòâîì îäíîãî êîíôèãóðàöèîííîãî ôàéëà, îáû÷íî
      ýòî ôàéë app/config/routing.yml (ñì. Ñîçäàíèå ìàðøðóòîâ âûøå). Íà ïðàêòèêå æå
      âû âåðîÿòíî çàõîòèòå çàãðóæàòü ìàðøðóòû èç äðóãèõ ìåñò, íàïðèìåð èç âàøèõ ïàêåòîâ.
      È ýòî ñòàíîâèòñÿ âîçìîæíûì ïðè ïîìîùè èìïîðòà ôàéëà ìàðøðóòîâ:
      ˆ

      YAML

      # app/config/routing.yml
      acme_hello:
      resource: "@AcmeHelloBundle/Resources/config/routing.yml"

      ˆ

      XML




      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing



      ˆ

      PHP

      // app/config/routing.php
      use Symfony\Component\Routing\RouteCollection;

      $collection = new RouteCollection();
      $collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php")
      return $collection;

      Ïðèìå÷àíèå: Ïðè èìïîðòå ðåñóðñîâ èç YAML, êëþ÷ (íàïðèìåð acme_hello) íå èìååò

      ïðàêòè÷åñêîãî çíà÷åíèÿ. Ïðîñòî óáåäèòåñü, ÷òî ýòîò êëþ÷ óíèêàëåí è íèãäå äàëåå íå
      ïåðåîïðåäåëÿåòñÿ.
      Êëþ÷ resource çàãðóæàåò óêàçàííûé ðåñóðñ ñ ìàðøðóòàìè.  äàííîì ïðèìåðå ðåñóðñ ýòî ïîëíûé ïóòü ê ôàéëó, ãäå @AcmeHelloBundle ýòî ÿðëûê, îçíà÷àþùèé ïóòü ê ïàêåòó.
      Èìïîðòèðóåìûé ôàéë ìîæåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:
      ˆ

      2.6.

      YAML

      Ìàðøðóòèçàöèÿ

      93

      Symfony Documentation, Âûïóñê 2.0

      # src/Acme/HelloBundle/Resources/config/routing.yml
      acme_hello:
      pattern: /hello/{name}
      defaults: { _controller: AcmeHelloBundle:Hello:index }

      ˆ

      XML




      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

      AcmeHelloBundle:Hello:index



      ˆ

      PHP

      // src/Acme/HelloBundle/Resources/config/routing.php
      use Symfony\Component\Routing\RouteCollection;
      use Symfony\Component\Routing\Route;
      $collection = new RouteCollection();
      $collection->add('acme_hello', new Route('/hello/{name}', array(
      '_controller' => 'AcmeHelloBundle:Hello:index',
      )));
      return $collection;

      Ìàðøðóòû èç ýòîãî ôàéëà àíàëèçèðóþòñÿ è çàãðóæàþòñÿ òàêæå, êàê è îñíîâíîé ôàéë.
      Ïðåôèêñ äëÿ èìïîðòèðóåìîãî ðåñóðñà

      Âû òàêæå ìîæåòå óêàçàòü ïðåôèêñ äëÿ èìïîðòèðóåìîãî ìàðøðóòà. Íàïðèìåð,
      ïðåäïîëîæèì, ÷òî âû õîòèòå ÷òîáû ìàðøðóò acme_hello èìåë ñëåäóþùèé âèä:
      /admin/hello/{name} âìåñòî îáû÷íîãî /hello/{name}:
      ˆ

      YAML

      # app/config/routing.yml
      acme_hello:
      resource: "@AcmeHelloBundle/Resources/config/routing.yml"
      prefix: /admin

      94

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      ˆ

      XML




      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing



      ˆ

      PHP

      // app/config/routing.php
      use Symfony\Component\Routing\RouteCollection;

      $collection = new RouteCollection();
      $collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php")
      return $collection;

      Ñòðîêà /admin òåïåðü áóäåò äîáàâëåíà âíà÷àëå êàæäîãî ìàðøðóòà, çàãðóæàåìîãî èç
      óêàçàííîãî ðåñóðñà:
      2.6.7 Îòîáðàæåíèå è Îòëàäêà ìàðøðóòîâ

      Ïî ìåðå äîáàâëåíèÿ è íàñòðîéêè ìàðøðóòîâ, áûëî áû óäîáíî èìåòü âîçìîæíîñòü âèçóàëèçèðîâàòü è ïîëó÷àòü äåòàëüíóþ èíôîðìàöèþ î íèõ. Äëÿ òîãî, ÷òîáû óâèäåòü
      âñå ìàðøðóòû âàøåãî ïðèëîæåíèÿ, âû ìîæåòå âîñïîëüçîâàòüñÿ êîíñîëüíîé êîìàíäîé
      router:debug. Âûïîëíèòå ýòó êîìàíäó èç êîðíÿ âàøåãî ïðîåêòà:
      php app/console router:debug

      Ýòà êîìàíäà îòîáðàçèò óäîáíûé ñïèñîê âñåõ íàñòðîåííûõ ìàðøðóòîâ âàøåãî ïðèëîæåíèÿ:
      homepage
      contact
      contact_process
      article_show
      blog
      blog_show

      ANY
      GET
      POST
      ANY
      ANY
      ANY

      /
      /contact
      /contact
      /articles/{culture}/{year}/{title}.{_format}
      /blog/{page}
      /blog/{slug}

      Âû òàêæå ìîæåòå ïîëó÷èòü áîëåå ïîäðîáíóþ èíôîðìàöèþ î êîíêðåòíîì ìàðøðóòå,
      óêàçàâ åãî èìÿ ïîñëå êîìàíäû router:debug:
      2.6.

      Ìàðøðóòèçàöèÿ

      95

      Symfony Documentation, Âûïóñê 2.0

      php app/console router:debug article_show

      2.6.8 Ãåíåðàöèÿ URL

      Ñèñòåìà ìàðøðóòèçàöèè òàêæå äîëæíà ïîçâîëÿòü ãåíåðèðîâàòü URL. Íà ïðàêòèêå,
      ìàðøðóòèçàöèÿ - ýòî äâóíàïðàâëåííàÿ ñèñòåìà: óñòàíàâëèâàåò êàê ñîîòâåòñòâèå URL
      ñ êîíòðîëëåðîì (+ ïàðàìåòðû), òàê è îáðàòíî - ïðåâðàùàåò ìàðøðóò (+ ïàðàìåòðû) â URL. Ìåòîäû :method:`Symfony\\Component\\Routing\\Router::match`
      è :method:`Symfony\\Component\\Routing\\Router::generate` ôîðìèðóþò ýòó
      äâóíàïðàâëåííóþ ñèñòåìó. Ðàññìîòðèì ìàðøðóò blog_show, îïèñàííûé âûøå:
      $params = $router->match('/blog/my-blog-post');
      // array('slug' => 'my-blog-post', '_controller' => 'AcmeBlogBundle:Blog:show')
      $uri = $router->generate('blog_show', array('slug' => 'my-blog-post'));
      // /blog/my-blog-post

      Äëÿ òîãî, ÷òîáû ñãåíåðèðîâàòü URL, âàì íåîáõîäèìî óêàçàòü èìÿ ìàðøðóòà
      (blog_show) è ïàðàìåòðû, èñïîëüçóåìûå â øàáëîíå ýòîãî ìàðøðóòà. Èìåÿ ýòó èíôîðìàöèþ, ìîæíî ñãåíåðèðîâàòü ëþáîé URL:
      class MainController extends Controller
      {
      public function showAction($slug)
      {
      // ...

      }

      }

      $url = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'));

      Â ñëåäóþùåé ñåêöèè âû óçíàåòå êàê ãåíåðèðîâàòü URL â øàáëîíå.

      Ñîâåò:

      Åñëè ôðîíòýíä âàøåãî ïðèëîæåíèÿ èñïîëüçóåò AJAX, âû âîçìîæíî çàõîòèòå èìåòü âîçìîæíîñòü ãåíåðèðîâàòü URL â JavaScript ïðè ïîìîùè âàøåé êîíôèãóðàöèè ìàðøðóòèçàòîðà. È âû òàêè ìîæåòå ýòî äåëàòü ïðè ïîìîùè ïàêåòà
      FOSJsRoutingBundle:
      var url = Routing.generate('blog_show', { "slug": 'my-blog-post});

      Ïîäðîáíåå ÷èòàéòå â äîêóìåíòàöèè ïàêåòà.

      96

      Ãëàâà 2.

      Êíèãà

      Symfony Documentation, Âûïóñê 2.0

      Ãåíåðàöèÿ Àáñîëþòíûõ URL

      Ïî óìîë÷àíèþ, ìàðøðóòèçàòîð ãåíåðèðóåò îòíîñèòåëüíûå URL (íàïðèìåð /blog). Äëÿ
      òîãî, ÷òîáû ñãåíåðèðîâàòü àáñîëþòíûé URL, ïðîñòî óêàæèòå true â êà÷åñòâå òðåòüåãî
      àðãóìåíòà ìåòîäà generate():
      $router->generate('blog_show', array('slug' => 'my-blog-post'), true);
      // http://www.example.com/blog/my-blog-post

      Ïðèìå÷àíèå: Õîñò, êîòîðûé èñïîëüçóåòñÿ äëÿ ãåíåðàöèè àáñîëþòíîãî URL - ýòî õîñò

      èç òåêóùåãî îáúåêòà Request. Ýòîò ïàðàìåòð îïðåäåëÿåòñÿ àâòîìàòè÷åñêè, îñíîâûâàÿñü
      íà èíôîðìàöèè î ñåðâåðå, êîòîðóþ ïðåäîñòàâëÿåò PHP. Ïðè ñîçäàíèè àáñîëþòíûõ URL
      äëÿ ñêðèïòîâ, çàïóùåííûõ èç êîìàíäíîé ñòðîêè, âàì íåîáõîäèìî âðó÷íóþ óñòàíîâèòü
      æåëàåìûé õîñò äëÿ îáúåêòà Request:

      $request->headers->set('HOST', 'www.example.com');

      Ãåíåðàöèÿ URL ñîäåðæàùèõ ñòðîêó çàïðîñà (Query String)

      Ìåòîä generate ïðèíèìàåò ìàññèâ çíà÷åíèé äëÿ ãåíåðàöèè URL. Åñëè âû ïåðåäàäèòå
      ëèøíèé (íå óêàçàííûé â îïðåäåëåíèè ìàðøðóòà) ïàðàìåòð, îí áóäåò äîáàâëåí êàê query
      string:
      $router->generate('blog', array('page' => 2, 'category' => 'Symfony'));
      // /blog/2?category=Symfony

      Ãåíåðàöèÿ URL â øàáëîíå

      Òèïè÷íîå ìåñòî, ãäå âàì ïîòðåáóåòñÿ ãåíåðèðîâàòü URL - ýòî øàáëîí. Âûïîëíèòü ýòó
      îïåðàöèþ ìîæíî, âîñïîëüçîâàâøèñü ôóíêöèåé-ïîìîùíèêîì:
      ˆ

      Twig


      Read this blog post.


      ˆ

      PHP


      Read this blog post.


      ˆ

      PHP





    • getCaption() ?>







     ñîñòàâ Symfony2 âõîäèò ìîùíûé ÿçûê øàáëîíîâ, íàçûâàåìûé Twig. Twig ïîçâîëÿåò ñîçäàâàòü ëàêîíè÷íûå è ÷èòàáåëüíûå øàáëîíû, êîòîðûå áîëåå óäîáíû äëÿ âåáäèçàéíåðîâ è, âî ìíîãîì, áîëåå ôóíêöèîíàëüíû, íåæåëè PHP øàáëîíû:



    Welcome to Symfony!


    {{ page_title }}






    Twig îïðåäåëÿåò äâà òèïà ñïåöèàëüíûõ ñèíòàêñè÷åñêèõ êîíñòðóêöèé:
    ˆ {{ ... }}: Íàïå÷àòàòü ÷òî-ëèáî: îòîáðàæàåò ïåðåìåííóþ èëè ðåçóëüòàò íåêîòîðîãî âûðàæåíèÿ;
    ˆ {% ... %}: Âûïîëíèòü ÷òî-ëèáî: òàã, êîòîðûé êîíòðîëèðóåò ëîãèêó øàáëîíà;
    îí èñïîëüçóåòñÿ äëÿ âûïîëíåíèÿ âûðàæåíèé, òàêèõ êàê öèêëû for.

    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    99

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Åñòü òàêæå òðåòèé òèï ñèíòàêñè÷åñêîé êîíñòðóêöèè äëÿ ñîçäàíèÿ êîì-

    ìåíòàðèåâ: {# ýòî êîììåíòàðèé #}. Ýòîò ñèíòàêñèñ ìîæåò áûòü èñïîëüçîâàí â êà÷åñòâå
    ìíîãîñòðî÷íîãî êîììåíòàðèÿ êàê PHP-àíàëîã /* comment */.

    Twig òàêæå ñîäåðæèò ôèëüòðû, êîòîðûå ìîäèôèöèðóþò êîíòåíò ïåðåä åãî îòîáðàæåíèåì. Ñëåäóþùèé ïðèìåð âûâåäåò ïåðåìåííóþ title â âåðõíåì ðåãèñòðå:
    {{ title | upper }}

    Twig ïî óìîë÷àíèþ ñîäåðæèò ìíîãî òàãîâ (tags) è ôèëüòðîâ (lters). Êðîìå ýòîãî âû
    ìîæåòå òàêæå ñîçäàâàòü ñâîè ñîáñòâåííûå ðàñøèðåíèÿ Twig, åñëè ýòî ïîòðåáóåòñÿ.

    Ñîâåò: Çàðåãèñòðèðîâàòü ðàñøèðåíèå Twig ïðîñòî: íóæíî ñîçäàòü ñåðâèñ è óêàçàòü
    åìó

    òàã

    twig.extension.

    Êàê âû óâèäèòå äàëåå, Twig òàêæå ïîääåðæèâàåò ôóíêöèè, è âû ñìîæåòå ëåãêî äîáàâëÿòü íîâûå ôóíêöèè. Íàïðèìåð, ñëåäóþùèé êîä èñïîëüçóåò ñòàíäàðòíûé òàã öèêëà
    for è ôóíêöèþ cycle äëÿ òîãî, ÷òîáû âûâåñòè äåñÿòü òàãîâ div ñ css-êëàññàìè odd è
    even:
    {% for i in 0..10 %}



    {% endfor %}

    Íà ïðîòÿæåíèè âñåé ãëàâû, ïðèìåðû øàáëîíîâ áóäóò îòîáðàæàòüñÿ êàê â Twigôîðìàòå, òàê è PHP.

    100

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïî÷åìó Twig?
    Twig øàáëîíû âûãëÿäÿò ïðîùå è íå îáðàáàòûâàþò PHP êîä. Ýòî ñäåëàíî íàìåðåííî: ñèñòåìà øàáëîíîâ Twig çàäóìàíà äëÿ áûñòðîãî ñîçäàíèÿ ïðåäñòàâëåíèÿ, à íå
    äëÿ ïðîãðàììíîé ëîãèêè. ×åì áîëüøå âû èñïîëüçóåòå Twig, òåì áîëåå âû áóäåòå
    öåíèòü åãî è òåì áîëåå áóäåòå ïîëó÷àòü ïîëüçû îò òàêîãî ðàçäåëåíèÿ. È âàñ áóäóò
    óâàæàòü âñå âåá-äèçàéíåðû â ìèðå.
    Twig òàêæå óìååò äåëàòü òî, ÷òî íå óìååò PHP, íàïðèìåð ïîëíîöåííîå íàñëåäîâàíèå
    øàáëîíîâ (Twig-øàáëîíû êîìïèëèðóþòñÿ â PHP-êëàññû, êîòîðûå íàñëåäóþòñÿ êàê
    ëþáûå äðóãèå êëàññû), êîíòðîëü ïðîáåëüíûõ ñèìâîëîâ, ïåñî÷íèöà (äëÿ êîíòðîëÿ
    âûïîëíåíèÿ ïîäîçðèòåëüíîãî êîäà â øàáëîíàõ), ðàñøèðåíèå ïîëüçîâàòåëüñêèìè
    ôèëüòðàìè è ôóíêöèÿìè. Twig èìååò ìíîãî íåáîëüøèõ îñîáåííîñòåé, êîòîðûå äåëàþò íàïèñàíèå øàáëîíîâ ïðîùå è ëàêîíè÷íåå. Âçãëÿíèòå íà ñëåäóþùèé ïðèìåð,
    êîòîðûé êîìáèíèðóåò öèêë ñ ëîãè÷åñêèì âûðàæåíèåì if:

      {% for user in users %}
    • {{ user.username }}

    • {% else %}
    • No users found

    • {% endfor %}


    Êýøèðîâàíèå Øàáëîíîâ â Twig

    Twig áûñòð. Êàæäûé Twig-øàáëîí êîìïèëèðóåòñÿ â îáû÷íûé PHP-êëàññ, êîòîðûé è
    îòîáðàæàåòñÿ âî âðåìÿ âûïîëíåíèÿ. Êîìïèëèðîâàííûå êëàññû ðàñïîëîæåíû â äèðåêòîðèè app/cache/{environment}/twig (çäåñü {environment} - ýòî íàçâàíèå îêðóæåíèÿ,
    íàïðèìåð dev èëè prod) è â íåêîòîðûõ ñëó÷àÿõ ìîæåò áûòü ïîëåçåí ïðè îòëàäêå. Îá
    îêðóæåíèÿõ ïîäðîáíåå íàïèñàíî â ðàçäåëå Îêðóæåíèÿ .
    Êîãäà àêòèâåí ðåæèì debug (êàê ïðàâèëî, â dev îêðóæåíèè), øàáëîí Twig áóäåò àâòîìàòè÷åñêè ïåðåêîìïèëèðîâàòüñÿ êàæäûé ðàç, êîãäà â í¼ì áûëè ïðîèçâåäåíû èçìåíåíèÿ. Ýòî îçíà÷àåò, ÷òî â ïðîöåññå ðàçðàáîòêè âû ñïîêîéíî ìîæåòå âûïîëíÿòü èçìåíåíèÿ
    â øàáëîíàõ è âèäåòü ýòè èçìåíåíèÿ áåç íåîáõîäèìîñòè ïîñòîÿííî ÷èñòèòü êýø.
    Êîãäà debug îòêëþ÷åí (êàê ïðàâèëî, â prod îêðóæåíèè), âû äîëæíû î÷èùàòü êýø â
    äèðåêòîðèè Twig äëÿ òîãî ÷òîáû øàáëîíû ïåðåêîìïèëèðîâàëèñü. Ïîìíèòå îá ýòîì ïðè
    âûêëàäêå âàøåãî ïðèëîæåíèÿ íà ñåðâåð.
    2.7.2 Íàñëåäîâàíèå øàáëîíîâ è Layout

    Îáû÷íî â ïðîåêòå øàáëîíû èñïîëüçóþò íåêîòîðîå êîëè÷åñòâî îáùèõ ýëåìåíòîâ, òàêèõ
    êàê øàïêà (header), ïîäâàë (footer), áîêîâûå ïàíåëè è ò.ï. Â Symfony2 ìû ðåøàåì ýòó
    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    101

    Symfony Documentation, Âûïóñê 2.0

    ïðîáëåìó ïî äðóãîìó: øàáëîí ìîæåò áûòü äåêîðèðîâàí äðóãèì øàáëîíîì. Ýòî ðàáîòàåò òî÷íî òàêæå êàê ñ êëàññàìè PHP: íàñëåäîâàíèå øàáëîíîâ ïîçâîëÿåò âàì ñîçäàâàòü
    áàçîâûé øàáëîí - ò.í. layout, êîòîðûé ñîäåðæèò âñå áàçîâûå ýëåìåíòû âàøåãî ñàéòà, íàçûâàåìûå áëîêàìè (àíàëîãè÷íî PHP-êëàññó ñ áàçîâûìè ìåòîäàìè). Äî÷åðíèé
    øàáëîí ìîæåò ðàñøèðÿòü áàçîâûé øàáëîí è ïåðåîïðåäåëÿòü ëþáîé åãî áëîê (àíàëîãè÷íî äî÷åðíèé PHP-êëàññ ìîæåò ïåðåîïðåäåëÿòü íåêîòîðûå ìåòîäû ðîäèòåëüñêîãî
    êëàññà).
    Ñíà÷àëà ñîçäàéòå ôàéë áàçîâîãî øàáëîíà (layout):
    ˆ

    Twig

    {# app/Resources/views/base.html.twig #}




    {% block title %} Test Application{% endblock %}




    {% block body %} {% endblock %}




    ˆ

    PHP






    <?php $view['slots']->output('title', 'Test Application') ?>




    output('body') ?>




    Ïðèìå÷àíèå: Õîòÿ îáñóæäåíèå íàñëåäîâàíèÿ øàáëîíîâ áóäåò âåñòèñü â òåðìèíàõ
    Twig, â PHP øàáëîíàõ èñïîëüçóåòñÿ òà æå ôèëîñîôèÿ.

    Ýòîò øàáëîí îïðåäåëÿåò áàçîâûé ñêåëåòîí HTML-äîêóìåíòà äëÿ ïðîñòîé ñòðàíèöû
    ñ äâóìÿ êîëîíêàìè. Â ýòîì ïðèìåðå îïðåäåëåíî òðè îáëàñòè {% block %} ( title,
    sidebar è body). Êàæäûé áëîê ìîæåò áûòü ïåðåîïðåäåë¼í äî÷åðíèì øàáëîíîì, èíà÷å
    áóäåò ñîõðàíåíà èçíà÷àëüíàÿ ðåàëèçàöèÿ ýòèõ áëîêîâ. Ýòî øàáëîí ìîæåò òàêæå áûòü
    îòîáðàæåí ñàìîñòîÿòåëüíî.  ýòîì ñëó÷àå áëîêè title, sidebar è body áóäóò ñîäåðæàòü
    ñâîè çíà÷åíèÿ ïî óìîë÷àíèþ.
    Äî÷åðíèé øàáëîí ìîæåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:
    ˆ

    Twig

    {# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
    {% extends '::base.html.twig' %}
    {% block title %} My cool blog posts{% endblock %}
    {% block body %}
    {% for entry in blog_entries %}

    {{ entry.title }}


    {{ entry.body }}


    {% endfor %}
    {% endblock %}

    ˆ

    PHP


    extend('::base.html.php') ?>
    set('title', 'My cool blog posts') ?>

    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    103

    Symfony Documentation, Âûïóñê 2.0

    start('body') ?>

    getTitle() ?>


    getBody() ?>



    stop() ?>

    Ïðèìå÷àíèå: Ðîäèòåëüñêèé øàáëîí îïðåäåëÿåòñÿ áëàãîäàðÿ ñïåöèàëüíîìó ñèíòàê-

    ñèñó ( ::base.html.twig), êîòîðûé óêàçûâàåò, ÷òî øàáëîí ðàñïîëîæåí â äèðåêòîðèè
    ïðîåêòà app/Resources/views. Ýòîò ñèíòàêñèñ áóäåò ðàññìàòðèâàòüñÿ â ñåêöèè Ïðàâèëà
    èìåíîâàíèÿ è ðàñïîëîæåíèÿ Øàáëîíîâ .

    Êëþ÷îì ê íàñëåäîâàíèþ øàáëîíîâ ÿâëÿåòñÿ òàã {% extends %}. Îí ñîîáùàåò äâèæêó
    øàáëîíèçàòîðà, ÷òî íåîáõîäèìî ñíà÷àëà âûïîëíèòü áàçîâûé øàáëîí, êîòîðûé íàñòðîèò
    îáùóþ ðàçìåòêó è îïðåäåëèò íåêîòîðîå êîëè÷åñòâî áëîêîâ. Ïîñëå ýòîãî áóäåò îòîáðàæàòüñÿ äî÷åðíèé øàáëîí, êîòîðûé óêàçûâàåò, ÷òî áëîêè ðîäèòåëÿ title è body áóäóò
    çàìåùàòüñÿ àíàëîãè÷íûìè áëîêàìè ïîòîìêà.  çàâèñèìîñòè îò çíà÷åíèå ïåðåìåííîé
    blog_entries, ðåçóëüòàò ìîæåò áûòü òàêèì:




    My cool blog posts




    My first post


    The body of the first post.


    Another post


    The body of the second post.






    Îáðàòèòå âíèìàíèå, ÷òî äî÷åðíèé øàáëîí íå îïðåäåëÿåò áëîê sidebar, ïîýòîìó èñïîëüçóåòñÿ çíà÷åíèå èç ðîäèòåëüñêîãî øàáëîíà. Êîíòåíò òàãà {% block %} âíóòðè ðîäèòåëüñêîãî øàáëîíà âñåãäà áóäåò èñïîëüçîâàòüñÿ ïî óìîë÷àíèþ.
    104

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Âû ìîæåòå èñïîëüçîâàòü ñòîëüêî óðîâíåé íàñëåäîâàíèÿ, ñêîëüêî âàì òðåáóåòñÿ.  ñëåäóþùåé ñåêöèè áóäåò ðàçîáðàíî òèïè÷íîå òð¼õóðîâíåâîå íàñëåäîâàíèå, à òàêæå áóäåò
    ðàññêàçàíî î òîì, êàê øàáëîíû ðàñïîëàãàþòñÿ âíóòðè Symfony2 ïðîåêòà.
    Ïðè ðàáîòå ñ íàñëåäîâàíèåì øàáëîíîâ åñòü íåñêîëüêî ïðàâèë, î êîòîðûõ íóæíî ïîìíèòü:
    ˆ Åñëè âû èñïîëüçóåòå òàã {% extends %} â øàáëîíå, îí äîëæåí áûòü ïåðâûì òàãîì
    â ýòîì øàáëîíå.
    ˆ ×åì áîëüøå òàãîâ {% block %} ó âàñ â áàçîâîì øàáëîíå, òåì ëó÷øå. Çàïîìíèòå,
    äî÷åðíèé øàáëîí íå îáÿçàí ðåàëèçîâûâàòü âñå áëîêè ðîäèòåëÿ, ïîýòîìó ñîçäàâàéòå ñòîëüêî áëîêîâ â áàçîâîì øàáëîíå, ñêîëüêî õîòèòå è óêàçûâàéòå äëÿ íèõ
    ðàçóìíûé êîíòåíò ïî óìîë÷àíèþ. ×åì áîëüøå áëîêîâ èìååò âàø áàçîâûé øàáëîí,
    òåì áîëåå ãèáêèì áóäåò âàø layout.
    ˆ Åñëè âû îáíàðóæèòå ïîâòîðÿþùèéñÿ êîíòåíò â íåñêîëüêèõ øàáëîíàõ, âåðîÿòíî
    ýòî îçíà÷àåò, ÷òî ëó÷øå áû ïåðåìåñòèòü ýòîò êîíòåíò â {% block %} ðîäèòåëüñêîãî
    øàáëîíà.  íåêîòîðûõ ñëó÷àÿõ, áîëåå óäà÷íûì ðåøåíèåì áóäåò ñîçäàíèå íîâîãî
    øàáëîíà è åãî ïîäêëþ÷åíèå (ñì. Ïîäêëþ÷åíèå äðóãèõ øàáëîíîâ ).
    ˆ Åñëè âàì íóæåí êîíòåíò áëîêà èç ðîäèòåëüñêîãî øàáëîíà, âû ìîæåòå èñïîëüçîâàòü ôóíêöèþ {{ parent() }}. Ýòî óäîáíî, â ñëó÷àå åñëè âû õîòèòå äîáàâèòü ê
    êîíòåíòó ðîäèòåëüñêîãî áëîêà ÷òî-ëèáî, âìåñòî òîãî, ÷òîáû ïîëíîñòüþ çàìåíÿòü
    åãî.
    {% block sidebar %}

    Table of Contents


    ...
    {{ parent() }}
    {% endblock %}

    2.7.3 Ïðàâèëà èìåíîâàíèÿ è ðàñïîëîæåíèÿ Øàáëîíîâ

    Ïî óìîë÷àíèþ, øàáëîíó ìîãóò ðàñïîëàãàòüñÿ â äâóõ ðàçëè÷íûõ ìåñòàõ:
    ˆ app/Resources/views/: Äèðåêòîðèÿ views ìîæåò ñîäåðæàòü øàáëîíû, îáùèå äëÿ
    âñåãî ïðèëîæåíèÿ (íàïðèìåð layout ïðèëîæåíèÿ), à òàêæå øàáëîíû, êîòîðûå ïåðåîïðåäåëÿþò øàáëîíû ïàêåòîâ (ñì. Ïåðåîïðåäåëåíèå øàáëîíîâ ïàêåòà );
    ˆ ïóòü/ê/ïàêåòó/Resources/views/: Êàæäûé ïàêåò ñîäåðæèò ñâîè ñîáñòâåííûå
    øàáëîíû â äèðåêòîðèè Resources/views (è å¼ ïîääèðåêòîðèÿõ). Áîëüøèíñòâî
    øàáëîíîâ áóäåò ðàñïîëàãàòüñÿ âíóòðè ïàêåòà.
    Symfony2 èñïîëüçóåò ñèíòàêñèñ bundle:controller:template äëÿ øàáëîíîâ. Ýòî ïîçâîëÿåò îïðåäåëÿòü ìåñòî ðàñïîëîæåíèÿ äëÿ ðàçëè÷íûõ òèïîâ øàáëîíîâ, êàæäûé èç
    êîòîðûõ ðàñïîëàãàåòñÿ â îïðåäåë¼ííîì ìåñòå:

    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    105

    Symfony Documentation, Âûïóñê 2.0

    ˆ AcmeBlogBundle:Blog:index.html.twig: Ýòà ôîðìà çàïèñè èñïîëüçóåòñÿ äëÿ
    øàáëîíà îïðåäåë¼ííîé ñòðàíèöû. Ýòè òðè ñòðîêè, ðàçäåë¼ííûå äâîåòî÷èåì (:)
    îçíà÷àåò ñëåäóþùåå:

     AcmeBlogBundle: (ïàêåò ), øàáëîí ðàñïîëîæåí âíóòðè ïàêåòà AcmeBlogBundle
    (íàïðèìåð src/Acme/BlogBundle);

     Blog: (êîíòðîëëåð ), óêàçûâàåò, ÷òî øàáëîí ðàñïîëîæåí âíóòðè ñóáäèðåêòîðèè Blog äèðåêòîðèè Resources/views;

     index.html.twig: (øàáëîí ), ñîáñòâåííî èìÿ ôàéëà - index.html.twig.
    ÷òî
    AcmeBlogBundle
    ðàñïîëîæåí
    â
    äèðåêòîðèè
    src/Acme/BlogBundle, ïîëíûé ïóòü ê ôàéëó øàáëîíà áóäåò ñëåäóþùèé:
    src/Acme/BlogBundle/Resources/views/Blog/index.html.twig.
    Ïðè

    óñëîâèè

    ˆ AcmeBlogBundle::layout.html.twig: Ýòà ôîðìà çàïèñè ñîîáùàåò, ÷òî ýòî áàçîâûé øàáëîí äëÿ ïàêåòà AcmeBlogBundle. Òàê êàê íàèìåíîâàíèå êîíòðîëëåðà íå
    óêàçàíî, øàáëîí ðàñïîëàãàåòñÿ â äèðåêòîðèè Resources/views/layout.html.twig
    ïàêåòà AcmeBlogBundle.
    ˆ ::base.html.twig: Ýòà ôîðìà çàïèñè ññûëàåòñÿ íà øàáëîí èëè ìàñòåð-øàáëîí
    (layout) óðîâíÿ âñåãî ïðèëîæåíèÿ. Îáðàòèòå âíèìàíèå, ÷òî ýòà ñòðîêà íà÷èíàåòñÿ
    ñ äâóõ äâîåòî÷èé (::), ÷òî îçíà÷àåò ñëåäóþùåå: øàáëîí íå ïðèíàäëåæèò íèêàêîìó
    ïàêåòó è ðàñïîëîæåí îí â äèðåêòîðèè app/Resources/views/.
     ñåêöèè Ïåðåîïðåäåëåíèå øàáëîíîâ ïàêåòà âû óçíàåòå êàê ëþáîé øàáëîí, íàõîäÿùèéñÿ, íàïðèìåð, â ïàêåòå AcmeBlogBundle, ìîæåò áûòü ïåðåîïðåäåë¼í ïóò¼ì ðàçìåùåíèÿ
    øàáëîíà ñ òåì æå èìåíåì â äèðåêòîðèè app/Resources/AcmeBlogBundle/views/. Ýòî
    äà¼ò âîçìîæíîñòü ïåðåîïðåäåëÿòü ëþáûå øàáëîíû ëþáîãî ñòîðîííåãî ïàêåòà.

    Ñîâåò: Íàäååìñÿ ñèíòàêñèñ èìåíîâàíèÿ øàáëîíîâ ïîêàçàëñÿ âàì çíàêîìûì - òàêîé æå
    ôîðìàò èñïîëüçóåòñÿ è äëÿ êîíòðîëëåðîâ (ñì.

    Øàáëîí Èìåíîâàíèÿ Êîíòðîëëåðà ).

    Ñóôôèêñû Øàáëîíîâ

    Ôîðìàò bundle:controller:template èìåíè øàáëîíà óêàçûâàåò ãäå øàáëîí íàõîäèòñÿ.
    Êàæäîå èìÿ øàáëîíà òàêæå èìååò äâà ðàñøèðåíèÿ, êîòîðûå îïðåäåëÿþò ôîðìàò è
    òèï øàáëîíèçàòîðà äëÿ ýòîãî øàáëîíà.
    ˆ AcmeBlogBundle:Blog:index.html.twig - HTML ôîðìàò, øàáëîíèçàòîð - Twig;
    ˆ AcmeBlogBundle:Blog:index.html.php - HTML ôîðìàò, øàáëîíèçàòîð - PHP;
    ˆ AcmeBlogBundle:Blog:index.css.twig - CSS ôîðìàò, øàáëîíèçàòîð - Twig.
    Ïî óìîë÷àíèþ, ëþáîé øàáëîí â Symfony2 ìîæåò áûòü íàïèñàí ëèáî íà Twig ëèáî íà
    PHP è ïîñëåäíÿÿ ÷àñòü ðàñøèðåíèÿ (.twig èëè .php) óêàçûâàåò, êàêîé èç ýòèõ äâóõ
    106

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    øàáëîíèçàòîðîâ áóäåò èñïîëüçîâàí. Ïåðâàÿ ÷àñòü ðàñøèðåíèÿ (.html, .css è ò.ä.) ýòî êîíå÷íûé ôîðìàò, êîòîðûé øàáëîí áóäåò ãåíåðèðîâàòü.  îòëè÷èå îò òèïà øàáëîíèçàòîðà, êîòîðûé îïðåäåëÿåò êàê Symfony2 áóäåò àíàëèçèðîâàòü øàáëîí, óêàçàíèå
    ôîðìàòà âñåãî ëèøü ñïîñîá îðãàíèçàöèè øàáëîíîâ, â ñëó÷àå åñëè îäèí ðåñóðñ ìîæåò
    áûòü îòîáðàæåí è êàê HTML (index.html.twig), è êàê XML (index.xml.twig) è â
    ëþáîì äðóãîì ôîðìàòå, êîòîðûé ìîæåò ïîòðåáîâàòüñÿ. Äîïîëíèòåëüíóþ èíôîðìàöèþ
    èùèòå â ñåêöèè Ôîðìàòû øàáëîíîâ .

    Ïðèìå÷àíèå: Äîñòóïíûå äâèæêè øàáëîíèçàòîðîâ ìîæíî íàñòðîèòü è äàæå äîáà-

    âèòü íîâûå. Äîïîëíèòåëüíóþ èíôîðìàöèþ èùèòå â ñåêöèè î Íàñòðîéêå

    øàáëîíèçàòî-

    ðà

    2.7.4 Òàãè è Õåëïåðû

    Ïðèìå÷àíèå: Ïðèìå÷àíèå ïåðåâîä÷èêà: çäåñü è äàëåå ôóíêöèÿ-ïîìîùíèê (helper)
    áóäåò îáîçíà÷åíà êàê õåëïåð.
    Âû óæå óçíàëè îñíîâû ñîçäàíèÿ øàáëîíîâ, êàê îíè èìåíóþòñÿ è êàê ðàáîòàåò íàñëåäîâàíèå øàáëîíîâ. Ñàìîå òÿæåëîå óæå ïîçàäè. Â ýòîé ñåêöèè âû óçíàåòå î ìíîæåñòâå
    èíñòðóìåíòîâ, ïîìîãàþùèõ âûïîëíÿòü òèïè÷íûå äëÿ øàáëîíîâ çàäà÷è, òàêèå êàê ïîäêëþ÷åíèå äðóãèõ øàáëîíîâ, ñîçäàíèå ññûëîê íà ñòðàíèöû è âñòàâêó èçîáðàæåíèé.
    Symfony2 ñîäåðæèò ìíîãî ñïåöèàëèçèðîâàííûõ òàãîâ è ôóíêöèé Twig, êîòîðûå óïðîùàþò ðàáîòó äèçàéíåðà øàáëîíîâ. PHP øàáëîíèçàòîð ïðåäîñòàâëÿåò ðàñøèðÿåìóþ ñèñòåìó õåëïåðîâ, êîòîðàÿ ïðåäîñòàâëÿåò ïîëåçíûå ôóíêöèè â ðàìêàõ øàáëîíà.
    Ìû óæå âèäåëè íåñêîëüêî âñòðîåííûõ â Twig òàãîâ ({% block %} & {% extends %}), à
    òàêæå ïðèìåð PHP-õåëïåðà ($view['slots']). Äàâàéòå æå óçíàåì è î äðóãèõ.
    Ïîäêëþ÷åíèå äðóãèõ øàáëîíîâ

    Íà ïðàêòèêå ó âàñ ÷àñòî áóäåò âîçíèêàòü ïîòðåáíîñòü ïîäêëþ÷èòü îäèí è òîò æå øàáëîí
    èëè æå ôðàãìåíò êîäà äëÿ ìíîãèõ ñòðàíèö. Íàïðèìåð, â ïðèëîæåíèè ñ íåêîòîðûìè ñòàòüÿìè, êîä øàáëîíà, îòîáðàæàþùèé îäíó ñòàòüþ, ìîæåò áûòü èñïîëüçîâàí íà ñòðàíèöå
    ñòàòüè, íà ñòðàíèöå, îòîáðàæàþùåé íàèáîëåå ïîïóëÿðíûå ñòàòüè èëè æå íà ñòðàíèöå
    ñî ñïèñêîì ïîñëåäíèõ ñòàòåé.
    Êîãäà âàì íåîáõîäèìî èñïîëüçîâàòü íåêîòîðûé áëîê PHP-êîäà, âû âûíîñèòå ýòîò êîä â
    êëàññ èëè â ôóíêöèþ. Òîæå âåðíî è äëÿ øàáëîíîâ. Ïåðåìåñòèâ êîä øàáëîíà, èñïîëüçóåìûé â íåñêîëüêèõ ìåñòàõ, â îòäåëüíûé ôàéë, âïîñëåäñòâèè îí ìîæåò áûòü ïîäêëþ÷åí
    ê ëþáîìó äðóãîìó øàáëîíó. Ñíà÷àëà ñîçäàéòå øàáëîí, êîòîðûé õîòèòå èñïîëüçîâàòü â
    íåñêîëüêèõ ìåñòàõ:
    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    107

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    Twig

    {# src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.twig #}

    {{ article.title }}




    {{ article.body }}



    ˆ

    PHP


    getTitle() ?>




    getBody() ?>



    Ïîäêëþ÷èòü ýòîò øàáëîí ê ëþáîìó äðóãîìó íåñëîæíî:
    ˆ

    Twig

    {# src/Acme/ArticleBundle/Resources/Article/list.html.twig #}
    {% extends 'AcmeArticleBundle::layout.html.twig' %}
    {% block body %}

    Recent Articles



    {% for article in articles %}
    {% include 'AcmeArticleBundle:Article:articleDetails.html.twig' with {'article': ar
    {% endfor %}
    {% endblock %}

    ˆ

    PHP


    extend('AcmeArticleBundle::layout.html.php') ?>
    start('body') ?>

    Recent Articles




    render('AcmeArticleBundle:Article:articleDetails.html.php', array

    stop() ?>

    Øàáëîí
    108

    ïîäêëþ÷àåòñÿ

    ïðè

    ïîìîùè

    òàãà

    {% include %}.

    Îáðàòèòå
    Ãëàâà 2.

    âíèìàÊíèãà

    Symfony Documentation, Âûïóñê 2.0

    íèå, ÷òî èìÿ øàáëîíà ñëåäóåò òèïîâûì êîíâåíöèÿì îá èìåíîâàíèè. Øàáëîí
    articleDetails.html.twig èñïîëüçóåò ïåðåìåííóþ article. Îíà ïåðåäà¼òñÿ â
    íåãî èç øàáëîíà list.html.twig ïðè ïîìîùè êîìàíäû with.

    Ñîâåò: Âûðàæåíèå {'article': article} - ýòî ñòàíäàðòíûé ñèíòàêñèñ äëÿ õýøåé
    (àññîöèàòèâíûõ ìàññèâîâ) â Twig. Åñëè âàì íóæíî ïåðåäàòü ìíîãî ýëåìåíòîâ - ìàññèâ
    áóäåò âûãëÿäåòü ñëåäóþùèì îáðàçîì: {'foo': foo, 'bar': bar}.

    Âíåäðåíèå êîíòðîëëåðîâ

     íåêîòîðûõ ñëó÷àÿõ, âàì ìîæåò ïîòðåáîâàòüñÿ íå÷òî áîëüøåå, íåæåëè ïðîñòî ïîäêëþ÷åíèå øàáëîíîâ. Ïîëîæèì, ó âàñ åñòü áîêîâàÿ ïàíåëü â øàáëîíå, êîòîðàÿ ñîäåðæèò òðè
    ñàìûõ ïîñëåäíèõ ñòàòüè. Ïîëó÷åíèå ýòèõ òð¼õ ñòàòåé ìîæåò âêëþ÷àòü çàïðîñû ê áàçå
    äàííûõ èëè æå âûïîëíåíèå íåêîòîðûõ äðóãèõ îïåðàöèé, êîòîðûå íåëüçÿ âûïîëíèòü
    íåïîñðåäñòâåííî èç øàáëîíà.
    Ðåøåíèåì â äàííîì ñëó÷àå ÿâëÿåòñÿ âñòðàèâàíèå â âàø øàáëîí ðåçóëüòàòà êîíòðîëëåðà öåëèêîì. Âî-ïåðâûõ, ñîçäàéòå êîíòðîëëåð, êîòîðûé áóäåò îòîáðàæàòü íåêîå ÷èñëî
    ïîñëåäíèõ ñòàòåé:
    // src/Acme/ArticleBundle/Controller/ArticleController.php
    class ArticleController extends Controller
    {
    public function recentArticlesAction($max = 3)
    {
    // make a database call or other logic to get the "$max" most recent articles
    $articles = ...;
    }

    }

    return $this->render('AcmeArticleBundle:Article:recentList.html.twig', array('articles'

    Øàáëîí recentList î÷åíü ïðîñò:
    ˆ

    Twig

    {# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
    {% for article in articles %}

    {{ article.title }}

    {% endfor %}

    ˆ
    2.7.

    PHP

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    109

    Symfony Documentation, Âûïóñê 2.0




    getTitle() ?>



    Ïðèìå÷àíèå: Îáðàòèòå âíèìàíèå, ÷òî ìû ñëóêàâèëè è çàõàðäêîäèëè URL ñòàòüè â
    ýòîì ïðèìåðå (/article/*slug*). Ýòî î÷åíü ïëîõàÿ ïðàêòèêà.  ñëåäóþùåé ñåêöèè âû
    óçíàåòå, êàê ïðàâèëüíî ñîçäàâàòü ññûëêè íà ñòðàíèöû ïðèëîæåíèÿ.

    Äëÿ òîãî ÷òîáû ïîäêëþ÷èòü êîíòðîëëåð, âàì íåîáõîäèìî ñîñëàòüñÿ íà íåãî, èñïîëüçóÿ
    ñòàíäàðòíûé ñèíòàêñèñ ëîãè÷åñêîãî èìåíè êîòðîëëåðà (bundle:controller:action):
    ˆ

    Twig

    {# app/Resources/views/base.html.twig #}
    ...


    ˆ

    PHP


    ...



    Âñÿêèé ðàç, êîãäà âû ïîíèìàåòå, ÷òî âàì íóæíà ïåðåìåííàÿ èëè äàííûå, ê êîòîðûì
    âû íå ìîæåòå ïîëó÷èòü äîñòóï èç øàáëîíà, îáÿçàòåëüíî ðàññìîòðèòå âàðèàíò ñ âñòðàèâàíèåì êîíòðîëëåðà. Êîíòðîëëåðû áûñòðî âûïîëíÿþòñÿ è ñïîñîáñòâóþò õîðîøåé îðãàíèçàöèè êîäà è åãî ïîâòîðíîìó èñïîëüçîâàíèþ.
    Ñîçäàíèå ññûëîê íà ñòðàíèöû

    Ñîçäàíèå ññûëîê íà äðóãèå ñòðàíèöû âàøåãî ïðèëîæåíèÿ - ýòî îäíà èç òèïè÷íûõ îïåðàöèé â øàáëîíå. Âìåñòî òîãî, ÷òîáû õàðäêîäèòü URL â øàáëîíå, èñïîëüçóéòå Twigôóíêöèþ path (â PHP - õåëïåð router) äëÿ ñîçäàíèÿ URL, îñíîâàííûõ íà êîíôèãóðàöèè ìàðøðóòèçàòîðà. Ïîòîì, åñëè âû çàõîòèòå èçìåíèòü URL íåêîòîðîé ñòðàíèöû,
    âàì âñåãî ëèøü ïîòðåáóåòñÿ èçìåíèòü êîíôèãóðàöèþ ìàðøðóòèçàòîðà. Øàáëîíû àâòîìàòè÷åñêè ñãåíåðèðóþò íîâûé URL.
    110

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ñíà÷àëà ñîçäàäèì ññûëêó íà ñòðàíèöó _welcome, êîòîðàÿ îïðåäåëÿåòñÿ ñëåäóþùåé
    êîíôèãóðàöèé ìàðøðóòèçàòîðà:
    ˆ

    YAML

    _welcome:
    pattern: /
    defaults: { _controller: AcmeDemoBundle:Welcome:index }

    ˆ

    XML


    AcmeDemoBundle:Welcome:index


    ˆ

    PHP

    $collection = new RouteCollection();
    $collection->add('_welcome', new Route('/', array(
    '_controller' => 'AcmeDemoBundle:Welcome:index',
    )));
    return $collection;

    Äëÿ ñîçäàíèÿ ññûëêè íà ñòðàíèöó èñïîëüçóéòå ôóíêöèþ path è ìàðøðóò:
    ˆ

    Twig

    Home

    ˆ

    PHP

    Home

    Êàê è îæèäàëîñü, îíà ñãåíåðèðóåò URL /. Äàâàéòå òåïåðü ïîñìîòðèì, êàê ýòî ðàáîòàåò
    äëÿ áîëåå ñëîæíûõ ìàðøðóòîâ:
    ˆ

    YAML

    article_show:
    pattern: /article/{slug}
    defaults: { _controller: AcmeArticleBundle:Article:show }

    ˆ

    XML


    AcmeArticleBundle:Article:show


    ˆ

    2.7.

    PHP

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    111

    Symfony Documentation, Âûïóñê 2.0

    $collection = new RouteCollection();
    $collection->add('article_show', new Route('/article/{slug}', array(
    '_controller' => 'AcmeArticleBundle:Article:show',
    )));
    return $collection;

     ýòîì ñëó÷àå, âàì íóæíî óêàçàòü è èìÿ ìàðøðóòà (article_show) è çíà÷åíèå ïàðàìåòðà {slug}. Èñïîëüçóÿ ýòîò ìàðøðóò, äàâàéòå âåðí¼ìñÿ ê øàáëîíó recentList èç
    ïðåäûäóùåé ñåêöèè è ñîçäàäèì ññûëêó íà ñòàòüþ ïðàâèëüíî:
    ˆ

    Twig

    {# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
    {% for article in articles %}

    {{ article.title }}

    {% endfor %}

    ˆ

    PHP



    Home

    Â PHP-øàáëîíàõ äëÿ ýòîãî íóæíî ïåðåäàòü òðåòèé àðãóìåíò â ìåòîä generate():
    Home

    Ññûëêè íà ðåñóðñû (assets)

    Øàáëîíû òàêæå ÷àñòî ññûëàþòñÿ íà êàðòèíêè, ñêðèïòû, ñòðàíèöû ñòèëåé è ïðî÷èå
    ðåñóðñû (çäåñü è äàëåå âìåñòî asset áóäåò èñïîëüçîâàí òåðìèí ðåñóðñ ). Êîíå÷íî, âû
    ìîæåòå õàðäêîäèòü ïóòè ê ðåñóðñàì (íàïðèìåð òàê /images/logo.png), íî Symfony2
    ïðåäëàãàåò èñïîëüçîâàòü áîëåå ãèáêóþ Twig-ôóíêöèþ asset:
    ˆ
    112

    Twig

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Symfony!


    ˆ

    PHP

    Symfony!


    {% endblock %}


    {# ... #}
    {% block javascripts %}

    {% endblock %}



    Ïðîùå íåêóäà! Íî ÷òî, åñëè âàì ïîòðåáóåòñÿ âêëþ÷èòü äîïîëíèòåëüíûé ôàéë ñòèëåé
    èëè ñêðèïò â äî÷åðíåì øàáëîíå? Íàïðèìåð, ïîëîæèì ó âàñ åñòü ñòðàíèöà êîíòàêòîâ
    è âàì íóæíî ïîäêëþ÷èòü ôàéë ñòèëåé contact.css ëèøü íà îäíîé ýòîé ñòðàíèöå.
    Âíóòðè øàáëîíà ñòðàíèöû contact íåîáõîäèìî âûïîëíèòü ñëåäóþùåå:
    {# src/Acme/DemoBundle/Resources/views/Contact/contact.html.twig #}
    {# extends '::base.html.twig' #}
    {% block stylesheets %}
    {{ parent() }}

    {% endblock %}
    {# ... #}

     äî÷åðíåì øàáëîíå âû ïðîñòî ïåðåîïðåäåëÿåòå áëîê stylesheets è ðàçìåùàåòå íîâûé
    ñòèëü âíóòðè ýòîãî áëîêà. Êîíå÷íî æå, ïîñêîëüêó âàì íóæíî äîáàâèòü ñòèëü, à íå
    èçìåíèòü åãî, âû äîëæíû èñïîëüçîâàòü ôóíêöèþ parent() äëÿ òîãî, ÷òîáû ïîëó÷èòü
    âñå ñòèëè èç ðîäèòåëüñêîãî øàáëîíà.
    Âû òàêæå ìîæåòå âêëþ÷àòü ðåñóðñû, ðàñïîëîæåííûå â äèðåêòîðèè Resources/public
    âàøèõ ïàêåòîâ. Âàì íóæíî áóäåò âûïîëíèòü êîìàíäó php app/console
    assets:install target [--symlink], êîòîðàÿ ñêîïèðóåò (èëè ñîçäàñò ñèìâîëè÷åñêóþ ññûëêó) ôàéëû â íóæíîå ìåñòî (target ïî óìîë÷àíèþ èìååò çíà÷åíèå web.



    Êîíå÷íûì ðåçóëüòàòîì ÿâëÿåòñÿ ñòðàíèöà, êîòîðàÿ âêëþ÷àåò êàê main.css, òàê è

    contact.css

    114

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    2.7.6 Íàñòðîéêà è èñïîëüçîâàíèå ñåðâèñà

    øàáëîíèçàòîðà

    Ñåðäöåì ñèñòåìû øàáëîíîâ Symfony2 ÿâëÿåòñÿ å¼ äâèæîê (Engine). Ýòî ñïåöèàëèçèðîâàííûé îáúåêò, êîòîðûé îòâå÷àåò çà îòîáðàæåíèå øàáëîíîâ è âîçâðàò èõ êîíòåíòà.
    Íàïðèìåð, êîãäà âû îòîáðàæàåòå øàáëîí èç êîíòðîëëåðà, âû èñïîëüçóåòå ñåðâèñ øàáëîíèçàòîðà:
    return $this->render('AcmeArticleBundle:Article:index.html.twig');

    Ýòîò êîä ýêâèâàëåíòåí ñëåäóþùåìó:
    $engine = $this->container->get('templating');
    $content = $engine->render('AcmeArticleBundle:Article:index.html.twig');
    return $response = new Response($content);

    Ñåðâèñ øàáëîíèçàòîðà ïðåäâàðèòåëüíî íàñòðîåí äëÿ àâòîìàòè÷åñêîé ðàáîòû âíóòðè
    Symfony2. Åñòåñòâåííî îí ìîæåò áûòü íàñòðîåí ÷åðåç ôàéë ñ íàñòðîéêàìè ïðèëîæåíèÿ:
    ˆ

    YAML

    # app/config/config.yml
    framework:
    # ...
    templating: { engines: ['twig'] }

    ˆ

    XML






    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    // ...
    'templating'
    => array(
    'engines' => array('twig'),
    ),
    ));

    Äëÿ íàñòðîéêè äîñòóïíî ìíîãî ðàçíûõ îïöèé, êîòîðûå îïèñàíû â Ïðèëîæåíèè î
    Êîíôèãóðàöèè.

    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    115

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Twig íåîáõîäèì äëÿ èñïîëüçîâàíèÿ âåá-ïðîôàéëåðà (à òàêæå ìíîãèõ

    ïàêåòîâ îò ñòîðîííèõ ðàçðàáîò÷èêîâ).

    2.7.7 Ïåðåîïðåäåëåíèå øàáëîíîâ ïàêåòà

    Ñîîáùåñòâî Symfony2 ãîðäèòñÿ ñîáîé çà òî, ÷òî åãî ýíòóçèàñòàìè ñîçäàíî è ïîääåðæèâàåòñÿ ìíîãî ðàçëè÷íûõ êà÷åñòâåííûõ ïàêåòîâ (ñì. Symfony2Bundles.org) íà ëþáîé
    ñëó÷àé æèçíè. Åñëè âû èñïîëüçóåòå ñòîðîííèå ïàêåòû, âàì ìîæåò ïîòðåáîâàòüñÿ èçìåíÿòü èõ øàáëîíû.
    Ïðåäïîëîæèì, âû ïîäêëþ÷èëè âîîáðàæàåìûé ïàêåò ñ îòêðûòûì èñõîäíûì êîäîì
    AcmeBlogBundle. Âû, â îáùåì-òî, âñåì äîâîëüíû, íî âàì õîòåëîñü áû çàìåíèòü ñòðàíèöó list äëÿ áëîãà, ÷òîáû íàñòðîèòü å¼ îòîáðàæåíèå ïîä âàøå ïðèëîæåíèå. Åñëè âû
    çàëåçåòå â êîíòðîëëåð Blog ïàêåòà AcmeBlogBundle, âû íàéä¼òå ñëåäóþùèé êîä:
    public function indexAction()
    {
    $blogs = // ïîëó÷åíèå çàïèñåé â áëîãå
    $this->render('AcmeBlogBundle:Blog:index.html.twig', array('blogs' => $blogs));

    }

    Êîãäà îòîáðàæàåòñÿ øàáëîí AcmeBlogBundle:Blog:index.html.twig Symfony2 íà ñàìîì äåëå èùåò åãî â äâóõ ìåñòàõ:
    1. app/Resources/AcmeBlogBundle/views/Blog/index.html.twig
    2. src/Acme/BlogBundle/Resources/views/Blog/index.html.twig
    Äëÿ òîãî ÷òîáû ïåðåîïðåäåëèòü øàáëîí èç ïàêåòà, ïðîñòî ñêîïèðóéòå åãî â äèðåêòîðèþ app/Resources/AcmeBlogBundle/views/Blog/index.html.twig (äèðåêòîðèþ
    app/Resources/AcmeBlogBundle íóæíî ñîçäàòü, òàê êàê ïî óìîë÷àíèþ å¼ òàì íå áóäåò).
    Òåïåðü âû ìîæåòå íàñòðàèâàòü øàáëîí ïî âàøåìó óñìîòðåíèþ.
    Ýòà ëîãèêà òàêæå ïðèìåíèìà ê áàçîâîìó øàáëîíó ïàêåòà. Ïîëîæèì ÷òî
    êàæäûé øàáëîí â ïàêåòå AcmeBlogBundle íàñëåäóåòñÿ îò áàçîâîãî øàáëîíà
    AcmeBlogBundle::layout.html.twig. Êàê è ðàíåå, Symfony2 áóäåò èñêàòü
    ýòîò øàáëîí â äâóõ ìåñòàõ:
    1. app/Resources/AcmeBlogBundle/views/layout.html.twig
    2. src/Acme/BlogBundle/Resources/views/layout.html.twig
    Êàê è ðàíåå, äëÿ ïåðåîïðåäåëåíèÿ øàáëîíà, ïðîñòî ñêîïèðóéòå åãî èç ïàêåòà
    app/Resources/AcmeBlogBundle/views/layout.html.twig. Ïîñëå ýòîãî âû âîëüíû ïðàâèòü åãî ïî ñâîåìó óñìîòðåíèþ.

    116

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Åñëè âû ñäåëàåòå øàã íàçàä, âû óâèäèòå, ÷òî Symfony2 âñåãäà íà÷èíàåò èñêàòü ôàéë
    øàáëîíà â äèðåêòîðèè app/Resources/{BUNDLE_NAME}/views/. Åñëè òàì åãî íåò, ïîèñê
    ïðîäîëæàåòñÿ â äèðåêòîðèè Resources/views ïàêåòà. Ýòî çíà÷èò, ÷òî âñå øàáëîíû ëþáîãî ïàêåòà ìîãóò áûòü ïåðåîïðåäåëåíû â äèðåêòîðèè ïàêåòà âíóòðè app/Resources.

    Ïåðåîïðåäåëåíèå øàáëîíîâ ÿäðà

    Òàê êàê Symfony2 ýòî òîæå ïàêåò, åãî øàáëîíû òàêæå ìîæíî ïåðåîïðåäåëèòü òåì æå
    îáðàçîì. Íàïðèìåð, TwigBundle ñîäåðæèò íåñêîëüêî øàáëîíîâ äëÿ ðàçëè÷íûõ èñêëþ÷èòåëüíûõ ñèòóàöèé è îøèáîê, êîòîðûå ìîãóò áûòü ïåðåîïðåäåëåíû, åñëè èõ ñêîïèðîâàòü èç äèðåêòîðèè Resources/views/Exception ïàêåòà TwigBundle â äèðåêòîðèþ
    app/Resources/TwigBundle/views/Exception.
    2.7.8 Òð¼õóðîâíåâîå íàñëåäîâàíèå

    Îäèí èç ñïîñîáîâ èñïîëüçîâàòü íàñëåäîâàíèå - òð¼õóðîâíåâûé ïîäõîä. Ýòîò ìåòîä çàìå÷àòåëüíî ðàáîòàåò ñ òðåìÿ ðàçëè÷íûìè òèïàìè øàáëîíîâ, êîòîðûå ìû óæå ðàññìîòðåëè:
    ˆ Ñîçäàéòå ôàéë app/Resources/views/base.html.twig, êîòîðûé ñîäåðæèò áàçîâóþ ðàçìåòêó ïðèëîæåíèÿ (êàê â ïðåäûäóùåì ïðèìåðå). Âíóòðè ïðèëîæåíèÿ òàêîé øàáëîí íàçûâàåòñÿ ::base.html.twig;
    ˆ Ñîçäàéòå ôàéë äëÿ êàæäîé ñåêöèè ñàéòà. Íàïðèìåð AcmeBlogBundle áóäåò ñîäåðæàòü øàáëîí AcmeBlogBundle::layout.html.twig, êîòîðûé âêëþ÷àåò òîëüêî
    ýëåìåíòû ñïåöèôè÷íûå äëÿ áëîãà;
    {# src/Acme/BlogBundle/Resources/views/layout.html.twig #}
    {% extends '::base.html.twig' %}
    {% block body %}

    Blog Application


    {% block content %} {% endblock %}
    {% endblock %}

    ˆ Ñîçäàéòå øàáëîíû äëÿ êàæäîé ñòðàíèöû è óíàñëåäóéòå èç îò øàáëîíà ñîîòâåòñòâóþùåé ñåêöèè (ïàêåòà). Íàïðèìåð, ñòðàíèöà index áóäåò âûçûâàòü ÷òî-òî
    òèïà AcmeBlogBundle:Blog:index.html.twig è îòîáðàæàòü çàïèñè áëîãà:
    {# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
    {% extends 'AcmeBlogBundle::layout.html.twig' %}
    {% block content %}
    {% for entry in blog_entries %}
    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    117

    Symfony Documentation, Âûïóñê 2.0

    {{ entry.title }}


    {{ entry.body }}


    {% endfor %}
    {% endblock %}

    Îáðàòèòå

    âíèìàíèå,

    ÷òî

    ýòîò

    øàáëîí íàñëåäóåòñÿ îò øàáëîíà ñåêöèè
    AcmeBlogBundle::layout.html.twig, êîòîðûé, â ñâîþ î÷åðåäü, íàñëåäóåòñÿ îò áàçîâîãî øàáëîíà ïðèëîæåíèÿ (::base.html.twig). Ýòî è åñòü òèïè÷íîå òð¼õóðîâíåâîå
    íàñëåäîâàíèå.
    Ïðè ñîçäàíèè ïðèëîæåíèÿ âû ìîæåòå âûáðàòü - áóäåòå ëè âû ñëåäîâàòü ýòîìó ìåòîäó
    èäè æå êàæäûé øàáëîí áóäåò íàñëåäîâàòüñÿ íàïðÿìóþ îò áàçîâîãî øàáëîíà ïðèëîæåíèÿ ({% extends '::base.html.twig' %}). Òð¼õóðîâíåâàÿ ìîäåëü ÿâëÿåòñÿ ïðîâåðåííûì è õîðîøî çàðåêîìåíäîâàâøèì ñåáÿ ìåòîäîì â ñòîðîííèõ ïàêåòàõ, òàê êàê áàçîâûé
    øàáëîí ïàêåòà ìîæåò áûòü ëåãêî ïåðåîïðåäåë¼í äëÿ òîãî ÷òîáû èñïîëüçîâàòü øàáëîí
    âàøåãî ïðèëîæåíèÿ.
    2.7.9 Ýêðàíèðîâàíèå

    Ïðè ñîçäàíèè HTML èç øàáëîíà, âñåãäà åñòü ðèñê, ÷òî ïåðåìåííàÿ øàáëîíà áóäåò
    ñîäåðæàòü HTML-êîä èëè îïàñíûé êëèåíòñêèé ñêðèïò. Â ðåçóëüòàòå ýòîò êîíòåíò ìîæåò ñëîìàòü HTML ðàçìåòêó ñòðàíèöû èëè æå ïîçâîëèòü çëîóìûøëåííèêó âûïîëíèòü
    Cross Site Scripting (XSS) àòàêó. Âîò êëàññè÷åñêèé ïðèìåð ýòîãî:
    ˆ

    Twig

    Hello {{ name }}

    ˆ

    PHP

    Hello

    Ïðåäñòàâüòå, ÷òî ïîëüçîâàòåëü ââ¼ë ñëåäóþùèé êîä â êà÷åñòâå èìåíè:


    Áåç ýêðàíèðîâàíèÿ, øàáëîí îòîáðàçèò:
    Hello

    à êëèåíò (áðàóçåð) âûïîëíèò JavaScript êîä è îòîáðàçèò îêîøêî alert.
    Ýòîò ïðèìåð âûãëÿäèò áåçîáèäíûì, íî ýòîò æå ïîëüçîâàòåëü ìîæåò òàêæå íàïèñàòü
    ñêðèïò, êîòîðûé âûïîëíèò âðåäîíîñíûå äåéñòâèÿ â çàùèù¼ííîé çîíå ïðèëîæåíèÿ êàê
    áóäòî áû ó íåãî áûëè ïðàâà íà ýòî.
    Îòâåòîì íà äàííóþ ïðîáëåìó ÿâëÿåòñÿ ýêðàíèðîâàíèå (output escaping). Ïðè íàëè÷èè
    ýêðàíèðîâàíèÿ, òîò æå êîä áóäåò îòîáðàæåí ñîâåðøåííî áåçîáèäíî:
    118

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Hello <script>alert('helloe')</script>

    Twig è PHP øàáëîíèçàòîðû ðåøàþò ýòó ïðîáëåìó ðàçëè÷íûì îáðàçîì. Åñëè âû èñïîëüçóåòå Twig, ýêðàíèðîâàíèå âêëþ÷åíî ïî óìîë÷àíèþ è âàøè øàáëîíû çàùèùåíû. Â
    PHP ýêðàíèðîâàíèå íå àâòîìàòè÷åñêîå è ïîäðàçóìåâàåò ÷òî âû âðó÷íóþ áóäåòå ýêðàíèðîâàòü äàííûå ïðè íåîáõîäèìîñòè.
    Ýêðàíèðîâàíèå â Twig

    Åñëè âû èñïîëüçóåòå øàáëîíû Twig, ýêðàíèðîâàíèå âêëþ÷åíî ïî óìîë÷àíèþ. Ýòî îçíà÷àåò, ÷òî âàø êîä çàùèù¼í îò íåîæèäàííûõ äåéñòâèé ïîëüçîâàòåëåé èç êîðîáêè. Ïî
    óìîë÷àíèþ, ýêðàíèðîâàíèå ïîäðàçóìåâàåò, ÷òî êîíòåíò áóäåò ýêðàíèðîâàí äëÿ HTML.
     íåêîòîðûõ ñëó÷àÿõ âàì ìîæåò ïîòðåáîâàòüñÿ îòêëþ÷èòü ýêðàíèðîâàíèå, êîãäà âû
    îòîáðàæàåòå ïåðåìåííóþ, êîòîðîé äîâåðÿåòå è êîòîðàÿ ñîäåðæèò HTML-ðàçìåòêó, êîòîðóþ íå íóæíî ýêðàíèðîâàòü. Ïîëîæèì, ÷òî àäìèíèñòðàòîð èìååò âîçìîæíîñòü ïèñàòü ñòàòüè, êîòîðûå ñîäåðæàò HTML-êîä. Ïî óìîë÷àíèþ Twig áóäåò ýêðàíèðîâàòü òåëî
    ñòàòüè. Äëÿ òîãî ÷òîáû îòîáðàçèòü åãî îáû÷íûì îáðàçîì íåîáõîäèìî äîáàâèòü ôèëüòð
    raw: {{ article.body | raw }}.
    Âû òàêæå ìîæåòå îòêëþ÷èòü ýêðàíèðîâàíèå âíóòðè áëîêà èëè æå äëÿ øàáëîíà öåëèêîì. Ïîäðîáíåå ýòî îïèñàíî â äîêóìåíòàöèè Twig: Output Escaping.
    Ýêðàíèðîâàíèå â PHP

    Ýêðàíèðîâàíèå â PHP øàáëîíàõ íå àâòîìàòè÷åñêîå. Ýòî îçíà÷àåò, ÷òî åñëè âû íå ýêðàíèðîâàëè ïåðåìåííóþ - âû íå çàùèùåíû. Äëÿ ýêðàíèðîâàíèÿ íåîáõîäèìî èñïîëüçîâàòü
    ñïåöèàëüíûé ìåòîä escape():
    Hello escape($name) ?>

    Ïî óìîë÷àíèþ, ìåòîä escape() ïîëàãàåò, ÷òî ïåðåìåííàÿ îòîáðàæàåòñÿ â HTML êîíòåêñòå (è ñîîòâåòñòâåííî ïåðåìåííàÿ ýêðàíèðóåòñÿ ÷òîáû áûòü áåçîïàñíîé â HTML).
    Âòîðîé àðãóìåíò ïîçâîëÿåò âàì èçìåíèòü êîíòåêñò. Íàïðèìåð, ÷òîáû âûâåñòè ÷òî-ëèáî
    â JavaScript èñïîëüçóéòå êîíòåêñò js:
    var myMsg = 'Hello escape($name, 'js') ?>';

    2.7.10 Ôîðìàòû øàáëîíîâ

    Øàáëîíû - ýòî îñíîâíîé ñïîñîá äëÿ îòîáðàæåíèÿ êîíòåíòà â ëþáîì ôîðìàòå.  áîëüøèíñòâå ñëó÷àåâ âû áóäåòå îòîáðàæàòü HTML êîíòåíò, íî øàáëîí òàêæå ìîæåò áûòü
    èñïîëüçîâàí äëÿ ãåíåðàöèè JavaScript, CSS, XML èëè æå ëþáîãî äðóãîãî ôîðìàòà íà
    âàø âûáîð.
    2.7.

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    119

    Symfony Documentation, Âûïóñê 2.0

    Íàïðèìåð, îäèí è òîò æå ðåñóðñ ìîæåò îòîáðàæàòüñÿ â ðàçíûõ ôîðìàòàõ. Äëÿ îòîáðàæåíèÿ èíäåêñíîé ñòðàíèöû ñòàòåé â XML ôîðìàòå, ïðîñòî äîáàâüòå ôîðìàò â èìÿ
    øàáëîíà:

    AcmeArticleBundle:Article:index.xml.twig

    ˆ

    XML template name :

    ˆ

    XML template lename :

    index.xml.twig

    Ïî ñóòè ýòî âñåãî ëèøü ñîãëàøåíèå ïî èìåíîâàíèþ øàáëîíîâ - øàáëîíû ðàçíûõ ôîðìàòîâ íå áóäóò îòîáðàæàòüñÿ ðàçíûìè ñïîñîáàìè.
    Âî ìíîãèõ ñëó÷àÿõ âàì ìîæåò ïîòðåáîâàòüñÿ îäèí è òîò æå êîíòðîëëåð îòîáðàçèòü â
    íåñêîëüêèõ ôîðìàòàõ â çàâèñèìîñòè îò ôîðìàòà çàïðîñà. Âû ìîæåòå âûïîëíèòü ýòî
    ñëåäóþùèì îáðàçîì:
    public function indexAction()
    {
    $format = $this->getRequest()->getRequestFormat();
    return $this->render('AcmeBlogBundle:Blog:index.'.$format.'.twig');

    }

    Ìåòîä getRequestFormat îáúåêòà Request ïî óìîë÷àíèþ âîçâðàùàåò html, íî ìîæåò
    òàêæå âîçâðàùàòü ëþáîé ôîðìàò çàïðîøåííûé ïîëüçîâàòåëåì. Ôîðìàò çàïðîñà ÷àñòî
    óïðàâëÿåòñÿ ìàðøðóòèçàòîðîì, ãäå ìàðøðóò ìîæåò áûòü íàñòðîåí òàêèì îáðàçîì ÷òîáû URL /contact âîçâðàùàë HTML à /contact.xml óñòàíàâëèâàë áû ôîðìàò çàïðîñà
    XML è êîíòðîëëåð áóäåò âîçâðàùàòü XML. Áîëåå ïîäðîáíî ýòîò âîïðîñ ðàññìàòðèâàåòñÿ â ãëàâå î Ìàðøðóòèçàöèè - Ïðîäâèíóòàÿ ìàðøðóòèçàöèÿ .
    Äëÿ ñîçäàíèÿ ññûëîê, êîòîðûå èñïîëüçóþò ïàðàìåòð ôîðìàòà - äîáàâüòå êëþ÷ _format
    ê õåøó ïàðàìåòðîâ:
    ˆ

    Twig


    PDF Version


    ˆ

    PHP

    */
    class Product
    {
    /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    protected $id;
    /**
    * @ORM\Column(type="string", length=100)
    */
    protected $name;
    124

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    /**
    * @ORM\Column(type="decimal", scale=2)
    */
    protected $price;

    }

    ˆ

    /**
    * @ORM\Column(type="text")
    */
    protected $description;

    YAML

    # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
    Acme\StoreBundle\Entity\Product:
    type: entity
    table: product
    id:
    id:
    type: integer
    generator: { strategy: AUTO }
    fields:
    name:
    type: string
    length: 100
    price:
    type: decimal
    scale: 2
    description:
    type: text

    ˆ

    XML


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">









    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    125

    Symfony Documentation, Âûïóñê 2.0



    Ñîâåò: Èìÿ òàáëèöû íåîáÿçàòåëüíî è åñëè îïóùåíî, òî îíî áóäåò îïðåäåëåíî àâòîìàòè÷åñêè, èñõîäÿ èç íàçâàíèÿ êëàññà-ñóùíîñòè.

    Doctrine ïîçâîëÿåò âûáèðàòü èç øèðîêîãî ðàçíîîáðàçèÿ ðàçëè÷íûõ òèïîâ ïîëåé, êàæäûé èç êîòîðûõ ñî ñâîèìè íàñòðîéêàìè. Çà èíôîðìàöèåé î äîñòóïíûõ òèïàõ îáðàùàéòåñü ê ðàçäåëó Ñïðàâêà ïî òèïàì ïîëåé â Doctrine .

    Ñì.òàêæå:
    Òàêæå ìîæíî îáðàòèòüñÿ ê Doctrine-îâîé Basic Mapping Documentation çà äåòàëüíîé
    èíôîðìàöèåé îá îòîáðàæåíèè. Åñëè áóäåòå èñïîëüçîâàòü àííîòàöèè, íåîáõîäèìî ïðåäâàðÿòü èõ, èñïîëüçóÿ ORM\ (íàïðèìåð, ORM\Column(..)), îá ýòîì íå ãîâîðèòñÿ â äîêóìåíòàöèè Doctrine. Òàêæå íàäî áóäåò âêëþ÷àòü use Doctrine\ORM\Mapping as ORM;
    óòâåðæäåíèå, êîòîðîå èìïîðòèðóåò ORM ïðåôèêñ äëÿ àííîòàöèé.

    Îñòîðîæíî: Áóäüòå îñòîðîæíû èìåíà êëàññîâ è ñâîéñòâ íå îòîáðàæàþòñÿ â çà-

    ùèù¼ííûå êëþ÷åâûå ñëîâà SQL (òàêèå êàê group èëè user). Íàïðèìåð, åñëè èìÿ
    ñóùíîñòíîãî êëàññà Group, òîãäà, ïî óìîë÷àíèþ, òàáëèöà áóäåò íàçâàíà group, ÷òî
    âûçîâåò îøèáêó SQL â íåêîòîðûõ äâèæêàõ. Îáðàòèòåñü ê äîêóìåíòàöèè ïî çàðåçåðâèðîâàííûì êëþ÷åâûì ñëîâàì SQL ÷òîáû óçíàòü êàê ëó÷øå ýêðàíèðîâàòü òàêèå
    èìåíà.

    Ïðèìå÷àíèå:

    Êîãäà èñïîëüçóåòñÿ äðóãàÿ áèáëèîòåêà èëè ïðîãðàììà (íàïðèìåð, Doxygen), èñïîëüçóþùàÿ àííîòàöèè, íåîáõîäèìî ïîìåñòèòü â êëàññ àííîòàöèþ
    @IgnoreAnnotation, ÷òîáû óêàçàòü êàêèå èç íèõ Symfony äîëæåí èãíîðèðîâàòü.
    Íàïðèìåð, ÷òîáû óáåðå÷ü @fn àííîòàöèþ îò âûäà÷è èñêëþ÷åíèÿ, äîáàâüòå ñëåäóþùåå:
    /**
    * @IgnoreAnnotation("fn")
    *
    */
    class Product

    Ñîçäàíèå ãåòòåðîâ è ñåòòåðîâ

    Òåïåðü, êîãäà Doctrine çíàåò êàê ñîõðàíèòü îáúåêò Product â áàçó äàííûõ, ñàì êëàññ
    ïîêà åù¼ áåñïîëåçåí. Òàê êàê Product âñåãî ëèøü îáû÷íûé PHP êëàññ, íåîáõîäèìî
    ñîçäàòü ãåòòåð è ñåòòåð ìåòîäû (íàïðèìåð, getName(), setName()) ÷òîáû ïîëó÷èòü äîñòóï ê åãî ñâîéñòâàì (ò. ê. ñâîéñòâà ÿâëÿþòñÿ protected). Ê ñ÷àñòüþ, Doctrine ìîæåò
    ñäåëàòü ýòî ïî êîìàíäå:
    126

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product

    Ýòà êîìàíäà óäîñòîâåðÿåòñÿ ÷òî âñå ãåòòåðû è ñåòòåðû ñîçäàíû äëÿ êëàññà Product.
    Îíà áåçîïàñíà - ìîæíî çàïóñêàòü å¼ ñíîâà è ñíîâà: êîìàíäà ëèøü ñîçäà¼ò ãåòòåðû è
    ñåòòåðû, êîòîðûõ åù¼ íåò (ò. î. îíà íå èçìåíèò ñóùåñòâóþùèå ìåòîäû).

    Îñòîðîæíî: Êîìàíäà doctrine:generate:entities ñîõðàíÿåò ðåçåðâíóþ êîïèþ

    èñõîäíîãî ôàéëà Product.php â Product.php~.  íåêîòîðûõ ñëó÷àÿõ, ïðèñóòñòâèå
    ýòîãî ôàéëà ìîæåò âûçâàòü îøèáêó Cannot redeclare class. Îí ìîæåò áûòü áåçîïàñíî
    óäàë¼í.
    Òàêæå ìîæíî ñîçäàòü âñå èçâåñòíûå ñóùíîñòè (íàïðèìåð, ëþáîé PHP êëàññ ñ èíôîðìàöèåé äëÿ îòîáðàæåíèÿ Doctrine) äëÿ áàíäëà èëè öåëîãî ïðîñòðàíñòâà èì¼í:
    php app/console doctrine:generate:entities AcmeStoreBundle
    php app/console doctrine:generate:entities Acme

    Ïðèìå÷àíèå: Doctrine íå èíòåðåñóåò ÿâëÿþòñÿ ëè ñâîéñòâà protected èëè private,

    èëè èìåþòñÿ ëèáî íåò ôóíêöèè ãåòòåðîâ èëè ñåòòåðîâ äëÿ ñâîéñòâà. Ãåòòåðû è ñåòòåðû ñîçäàþòñÿ çäåñü òîëüêî ïîòîìó ÷òî îíè ïîíàäîáÿòñÿ äëÿ âçàèìîäåéñòâèÿ ñ PHP
    îáúåêòîì.

    Ñîçäàíèå òàáëèö/ñõåìû äëÿ áàçû äàííûõ

    Òåïåðü åñòü óäîáíûé êëàññ Product ñ èíôîðìàöèåé äëÿ îòîáðàæåíèÿ, êîòîðûé Doctrine
    òî÷íî çíàåò êàê ñîõðàíèòü. Êîíå÷íî, ïîêà íåò ñîîòâåñòâóþùåé òàáëèöû product â áàçå
    äàííûõ. Ê ñ÷àñòüþ, Doctrine ìîæåò àâòîìàòè÷åñêè ñîçäàòü âñå òàáëèöû áàçû äàííûõ,
    íåîáõîäèìûå äëÿ âñåõ èçâåñòíûõ ñóùíîñòåé ïðèëîæåíèÿ. ×òîáû ñîçäàòü èõ, âûïîëíèòå:
    php app/console doctrine:schema:update --force

    Ñîâåò: Ýòà êîìàíäà íåîáû÷àéíî ìîùíàÿ. Îíà ñðàâíèâàåò êàê äîëæíà âûãëÿäåòü áàçà

    äàííûõ (îñíîâûâàÿñü íà èíôîðìàöèè îá îòîáðàæåíèè äëÿ ñóùíîñòåé) ñ òåì, êàê îíà
    âûãëÿäèò íà ñàìîì äåëå, è ñîçäà¼ò SQL âûðàæåíèÿ, íåîáõîäèìûå äëÿ îáíîâëåíèÿ áàçû
    äàííûõ äî òîãî âèäà, êàêîé îíà äîëæíà áûòü. Äðóãèìè ñëîâàìè, äîáàâèâ íîâîå ñâîéñòâî
    ñ ìåòàäàííûìè îòîáðàæåíèÿ â Product è çàïóñòèâ å¼ ñíîâà, îíà ñîçäàñò âûðàæåíèå
    alter table, íåîáõîäèìîå äëÿ äîáàâëåíèÿ ýòîãî íîâîãî ñòîëáöà ê ñóùåñòâóþùåé òàáëèöå
    product.
    Ëó÷øèé ñïîñîá ïîëó÷èòü ïðåèìóùåñòâà îò å¼ ôóíêöèîíàëüíîñòè ýòî ìèãðàöèè, êîòîðûå ïîçâîëÿþò ñîçäàâàòü ýòè SQL âûðàæåíèÿ è õðàíèòü èõ â ìèãðàöèîííûõ êëàññàõ,
    êîòîðûå ìîãóò ñèñòåìàòè÷åñêè çàïóñêàòüñÿ íà ïðîäàêøí ñåðâåðå ÷òîáû ñîîòâåñòâîâàòü
    ñõåìå áàçû äàííûõ è èçìåíÿòü å¼ áåçîïàñíî è íàä¼æíî.
    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    127

    Symfony Documentation, Âûïóñê 2.0

    Òåïåðü áàçà äàííûõ èìååò ïîëíîöåííóþ òàáëèöó product ñî ñòîëáöàìè, ñîîòâåñòâóþùèìè óêàçàííûì ìåòàäàííûì.
    Ñîõðàíåíèå îáúåêòîâ â áàçå äàííûõ

    Òåïåðü, êîãäà åñòü îòîáðàæ¼ííàÿ ñóùíîñòü Product è ñîîòâåñòâóþùàÿ òàáëèöà product,
    âñ¼ ãîòîâî ê ñîõðàíåíèþ äàííûõ â áàçó. Âíóòðè êîíòðîëëåðà ýòî î÷åíü ïðîñòî. Äîáàâüòå
    ñëåäóþùèé ìåòîä â DefaultController áàíäëà:
    1
    2
    3
    4

    // src/Acme/StoreBundle/Controller/DefaultController.php
    use Acme\StoreBundle\Entity\Product;
    use Symfony\Component\HttpFoundation\Response;
    // ...

    5
    6
    7
    8
    9
    10
    11

    public function createAction()
    {
    $product = new Product();
    $product->setName('A Foo Bar');
    $product->setPrice('19.99');
    $product->setDescription('Lorem ipsum dolor');

    12

    $em = $this->getDoctrine()->getEntityManager();
    $em->persist($product);
    $em->flush();

    13
    14
    15
    16

    return new Response('Created product id '.$product->getId());

    17
    18

    }

    Ïðèìå÷àíèå: Åñëè âû ñëåäóåòå ýòîìó ïðèìåðó, íåîáõîäèìî ñîçäàòü ìàðøðóò, óêàçûâàþùèé íà ýòî äåéñòâèå, ÷òîáû óâèäåòü åãî â ðàáîòå.
    Ïðîéä¼ìñÿ ïî ïðèìåðó:
    ˆ ñòðîêè 8-11  ýòîé ÷àñòè, áåð¼òñÿ ýêçåìïëÿð îáúåêòà $product è ñ íèì ïðîâîäèòñÿ ðàáîòà êàê ñ ëþáûì äðóãèì íîðìàëüíûì PHP îáúåêòîì;
    ˆ ñòðîêà 13 Ýòà ñòðîêà ïîëó÷àåò Doctrine-îâûé îáúåêò entity manager, îòâåñòâåííûé çà óïðàâëåíèå ïðîöåññàìè ñîõðàíåíèÿ è ïîëó÷åíèÿ îáúåêòîâ èç áàçû äàííûõ;
    ˆ ñòðîêà 14 Ìåòîä persist() ñîîáùàåò Doctrine êîìàíäó íà óïðàâëåíèå îáúåêòîì
    $product. Îíà íå âûçûâàåò ñîçäàíèå çàïðîñà ê áàçå äàííûõ (ïîêà).
    ˆ ñòðîêà 15 Êîãäà âûçûâàåòñÿ ìåòîä flush(), Doctrine ïðîñìàòðèâàåò âñå îáúåêòû,
    êîòîðûìè îíà óïðàâëÿåò, ÷òîáû óçíàòü, íàäî ëè ñîõðàíèòü èõ â áàçó äàííûõ.

    128

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

     ýòîì ïðèìåðå îáúåêò $product åù¼ íå áûë ñîõðàí¼í, ïîýòîìó entity manager
    âûïîëíèò çàïðîñ INSERT è áóäåò ñîçäàíà ñòðîêà â òàáëèöå product.

    Ïðèìå÷àíèå: Ôàêòè÷åñêè, ò. ê. Doctrine çíàåò îáî âñåõ óïðàâëÿåìûõ ñóùíîñòÿõ, êî-

    ãäà âûçûâàåòñÿ ìåòîä flush(), îíà ïðîùèòûâàåò îáùèé íàáîð èçìåíåíèé è âûïîëíÿåò
    íàèáîëåå ýôôåêòèâíûé è âîçìîæíûé çàïðîñ èëè çàïðîñû. Íàïðèìåð, åñëè ñîõðàíÿåòñÿ
    100 îáúåêòîâ Product è âïîñëåäñòâèè âûçûâàåòñÿ flush(), òî Doctrine ñîçäàñò åäèíñòâåííîå ïîäãîòîâëåííîå âûðàæåíèå è ïîâòîðíî èñïîëüçóåò åãî äëÿ êàæäîé âñòàâêè.
    Ýòîò ïàòòåðí íàçûâàåòñÿ Unit of Work è èñïîëüçóåòñÿ ïîòîìó÷òî áûñòð è ýôôåêòèâåí.
    Ïðè ñîçäàíèè èëè îáíîâëåíèè îáúåêòîâ ðàáî÷èé ïðîöåññ âñåãäà îäèíàêîâ.  ñëåäóþùåì
    ðàçäåëå âû óâèäèòå ÷òî Doctrine äîñòàòî÷íî óìíà ÷òîáû àâòîìàòè÷åñêè âûäàòü çàïðîñ
    UPDATE åñëè çàïèñü óæå ñóùåñòâóåò â áàçå äàííûõ.

    Ñîâåò: Doctrine ïðåäëàãàåò áèáëèîòåêó, ïîçâîëÿþùóþ ïðîãðàììíî çàãðóæàòü òå-

    ñòîâûå äàííûå â ïðîåêò (ò. í. xture data). Èíôîðìàöèþ ìîæíî óçíàòü â
    /bundles/DoctrineFixturesBundle/index.

    Ïîëó÷åíèå îáúåêòîâ èç áàçû äàííûõ

    Ïîëó÷åíèå îáúåêòà íàçàä èç áàçû äàííûõ åù¼ ïðîùå. Íàïðèìåð, ïðåäñòàâèì ÷òî íàñòðîåí ìàðøðóò, îòîáðàæàþùèé îïðåäåë¼ííûé Product, îñíîâûâàÿñü íà åãî çíà÷åíèè
    id:
    public function showAction($id)
    {
    $product = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product')
    ->find($id);
    if (!$product) {
    throw $this->createNotFoundException('No product found for id '.$id);
    }
    }

    // äåëàåò ÷òî-íèáóäü, íàïðèìåð ïåðåäà¼ò îáúåêò $product â øàáëîí

    Êîãäà çàïðàøèâàåòñÿ îáúåêò îïðåäåë¼ííîãî òèïà, âñåãäà èñïîëüçóåòñÿ òàê íàçûâàåìûé
    ðåïîçèòîðèé. Ìîæíî ïðåäñòàâèòü ðåïîçèòîðèé êàê PHP êëàññ, ÷üÿ ðàáîòà ñîñòîèò â
    ïðåäîñòàâëåíèè ïîìîùè â ïîëó÷åíèè ñóùíîñòåé îïðåäåë¼ííîãî êëàññà. Ìîæíî ïîëó÷èòü äîñòóï ê îáúåêòó-ðåïîçèòîðèþ äëÿ êëàññà-ñóùíîñòè ÷åðåç:
    $repository = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product');
    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    129

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Ñòðîêà AcmeStoreBundle:Product - ýòî ñîêðàùåíèå, êîòîðîå ìîæ-

    íî èñïîëüçîâàòü â Doctrine âìåñòî ïîëíîãî èìåíè êëàññà äëÿ ñóùíîñòè (íàïðèìåð,
    Acme\StoreBundle\Entity\Product). Îíî áóäåò ðàáîòàòü ïîêà ñóùíîñòü íàõîäèòñÿ â
    ïðîñòàíñòâå èì¼í Entity âàøåãî áàíäëà.

    Êîãäà èìååòñÿ ðåïîçèòîðèé, ó âàñ åñòü äîñòóï êî âñåì âèäàì ïîëåçíûõ ìåòîäîâ:
    // çàïðîñ ïî ïåðâè÷íîìó êëþ÷ó (îáû÷íî "id")
    $product = $repository->find($id);
    // äèíàìè÷åñêèå èìåíà ìåòîäîâ, èñïîëüçóþùèåñÿ äëÿ ïîèñêà ïî çíà÷åíèþ ñòîëáöîâ
    $product = $repository->findOneById($id);
    $product = $repository->findOneByName('foo');
    // èùåò *âñå* ïðîäóêòû
    $products = $repository->findAll();
    // èùåò ãðóïïó ïðîäóêòîâ, îñíîâûâàÿñü íà ïðîèçâîëüíîì çíà÷åíèè ñòîëáöà
    $products = $repository->findByPrice(19.99);

    Ïðèìå÷àíèå: Êîíå÷íî, òàêæå ìîæíî çàäàâàòü ñëîæíûå çàïðîñû, î êîòîðûõ âû óçíàåòå áîëüøå â ðàçäåëå

    Çàïðàøèâàíèå îáúåêòîâ .

    Òàêæå ìîæíî èñïîëüçîâàòü ïðåèìóùåñòâà ïîëåçíûõ ìåòîäîâ findBy è findOneBy äëÿ
    ë¼ãêîãî èçâëå÷åíèÿ îáúåêòîâ, îñíîâûâàÿñü íà ìíîãî÷èñëåííûõ óñëîâèÿõ:
    // çàïðîñ îäíîãî ïðîäóêòà, ïîäõîäÿùåãî ïî çàäàííûì èìåíè è öåíå
    $product = $repository->findOneBy(array('name' => 'foo', 'price' => 19.99));
    // çàïðîñ âñåõ ïðîäóêòîâ, ïîäõîäÿùèõ ïî èìåíè è îòñîðòèðîâàííûõ ïî öåíå
    $product = $repository->findBy(
    array('name' => 'foo'),
    array('price' => 'ASC')
    );

    Ñîâåò: Êîãäà âûäà¼òñÿ ëþáàÿ ñòðàíèöà, ìîæíî óâèäåòü ñêîëüêî çàïðîñîâ áûëî ñäåëàíî â íèæíåì ïðàâîì óãëó íà ïàíåëè èíñòðóìåíòîâ web debug.

    130

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Åñëè êëèêíóòü íà èêîíêå, îòêðîåòñÿ ïðîôèëèðîâùèê, ïîêàçûâàþùèé òî÷íûå çàïðîñû,
    êîòîðûå áûëè ñäåëàíû.

    Îáíîâëåíèå îáúåêòà

    Êîãäà âû ïîëó÷èëè îáúåêò èç Doctrine, îáíîâèòü åãî òàêæå ïðîñòî. Ïðåäïîëîæèì, åñòü
    ìàðøðóò, ñâÿçûâàþùèé id ïðîäóêòà ñ äåéñòâèåì îáíîâëåíèÿ â êîíòðîëëåðå:
    public function updateAction($id)
    {
    $em = $this->getDoctrine()->getEntityManager();
    $product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
    if (!$product) {
    throw $this->createNotFoundException('No product found for id '.$id);
    }
    $product->setName('New product name!');
    $em->flush();
    return $this->redirect($this->generateUrl('homepage'));

    }

    Îáíîâëåíèå îáúåêòà âêëþ÷àåò òðè øàãà:
    1. ïîëó÷åíèå îáúêòà èç Doctrine;
    2. èçìåíåíèå îáúåêòà;
    3. âûçîâ flush() èç entity manager
    Çàìåòüòå, ÷òî â âûçîâå $em->persist($product) íåò íåîáõîäèìîñòè. Âñïîìíèòå, ÷òî
    ýòîò ìåòîä ëèøü ñîîáùàåò Doctrine ÷òî íóæíî óïðàâëÿòü èëè íàáëþäàòü çà îáúåêòîì
    $product.  äàííîé æå ñèòóàöèè, ò. ê. îáúåêò $product ïîëó÷åí èç Doctrine, îí óæå
    ÿâëÿåòñÿ óïðàâëÿåìûì.

    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    131

    Symfony Documentation, Âûïóñê 2.0

    Óäàëåíèå îáúåêòà

    Óäàëåíèå îáúåêòà î÷åíü ïîõîæå, íî òðåáóåò âûçîâà ìåòîäà remove() èç entity manager:
    $em->remove($product);
    $em->flush();

    Êàê è îæèäàëîñü, ìåòîä remove() óâåäîìëÿåò Doctrine î òîì, ÷òî âàì õî÷åòñÿ óäàëèòü
    óêàçàííóþ ñóùíîñòü èç áàçû äàííûõ. Òåì íå ìåíåå, ôàêòè÷åñêèé çàïðîñ DELETE íå
    âûçûâàåòñÿ äî òåõ ïîð, ïîêà ìåòîä flush() íå çàïóùåí.
    2.8.2 Çàïðàøèâàíèå îáúåêòîâ

    Âû óæå âèäåëè êàê îáúåêò-ðåïîçèòîðèé ïîçâîëÿåò âûïîëíÿòü ïðîñòûå çàïðîñû áåç
    êàêîé-ëèáî ðàáîòû:
    $repository->find($id);
    $repository->findOneByName('Foo');

    Êîíå÷íî, Doctrine òàêæå ïîçâîëÿåò ïèñàòü áîëåå ñëîæíûå çàïðîñû, èñïîëüçóÿ Doctrine
    Query Language (DQL). DQL ïîõîæ íà SQL çà èñêëþ÷åíèåì òîãî, ÷òî ñëåäóåò ïðåäñòàâèòü ÷òî çàïðàøèâàþòñÿ îäèí èëè íåñêîëüêî îáúåêòîâ èç êëàññà-ñóùíîñòè (íàïðèìåð,
    Product) âìåñòî ñòðîê èç òàáëèöû (íàïðèìåð, product).
    Çàïðàøèâàòü èç Doctrine ìîæíî äâóìÿ ñïîñîáàìè: íàïèñàíèåì ÷èñòûõ Doctrine çàïðîñîâ ëèáî èñïîëüçîâàíèåì Doctrine-îâîãî Query Builder.
    Çàïðàøèâàíèå îáúåêòîâ ÷åðåç DQL

    Ïðåäñòàâüòå ÷òî íóæíî çàïðîñèòü ïðîäóêòû, íî âåðíóòü òîëüêî òå, ÷üÿ öåíà áîëüøå
    ÷åì 19.99 è ïî ïîðÿäêó îò äåø¼âîãî äî ñàìîãî äîðîãîãî. Âíóòðè êîíòðîëëåðà ñäåëàéòå
    ñëåäóþùåå:
    $em = $this->getDoctrine()->getEntityManager();
    $query = $em->createQuery(
    'SELECT p FROM AcmeStoreBundle:Product p WHERE p.price > :price ORDER BY p.price ASC'
    )->setParameter('price', '19.99');
    $products = $query->getResult();

    Åñëè âàì óäîáíî ñ SQL, òî DQL äîëæåí áûòü òàêæå ïîíÿòåí. Íàèáîëüøåå ðàçëè÷èå â
    òîì, ÷òî íàäî äóìàòü òåðìèíàìè îáúåêòîâ, à íå ñòðîê â áàçå äàííûõ. Ïî ýòîé ïðè÷èíå,
    âû âûáèðàåòå èç AcmeStoreBundle:Product è ïðèñâàèâàåòå åìó ïñåâäîíèì p.

    132

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ìåòîä getResult() âîçâðàùàåò ìàññèâ ðåçóëüòàòîâ. Åñëè æå íóæåí ëèøü îäèí îáúåêò
    ìîæíî âîñïîëüçîâàòüñÿ ìåòîäîì getSingleResult():
    $product = $query->getSingleResult();

    Îñòîðîæíî:

    Ìåòîä
    getSingleResult()
    âûáðàñûâàåò
    èñêëþDoctrine\ORM\NoResultException
    åñëè
    íåò
    ðåçóëüòàòîâ
    è
    Doctrine\ORM\NonUniqueResultException åñëè âîçâðàùàåòñÿ áîëüøå îäíîãî
    ðåçóëüòàòà. Åñëè èñïîëüçóåòñÿ ýòîò ìåòîä, âîçìîæíî ïðèä¼òñÿ îáåðíóòü åãî â
    try-catch áëîê è óáåäèòüñÿ â òîì, ÷òî âîçâðàùàåòñÿ òîëüêî îäèí ðåçóëüòàò (åñëè
    çàïðàøèâàåòñÿ ÷òî-òî, ÷òî ìîæåò âåðîÿòíî âåðíóòü áîëåå îäíîãî ðåçóëüòàòà):

    ÷åíèå

    $query = $em->createQuery('SELECT ....')
    ->setMaxResults(1);
    try {
    $product = $query->getSingleResult();
    } catch (\Doctrine\Orm\NoResultException $e) {
    $product = null;
    }
    // ...

    Ñèíòàêñèñ DQL íåâåðîÿòíî ìîùíûé, ïîçâîëÿåò ëåãêî óñòàíàâëèâàòü îáúåäèíåíèÿ ìåæäó ñóùíîñòÿìè (òåìà îòíîøåíèé áóäåò ðàñêðûòà ïîçæå), ãðóïïàìè è ò. ä. Äîïîëíèòåëüíàÿ èíôîðìàöèÿ â äîêóìåíòàöèè Doctrine Doctrine Query Language.

    Íàñòðîéêà ïàðàìåòðîâ
    Çàìåòêà î ìåòîäå setParameter(). Ðàáîòàÿ ñ Doctrine, õîðîøèì òîíîì ÿâëÿåòñÿ
    óêàçàíèå ëþáûõ âíåøíèõ çíà÷åíèé ÷åðåç placeholders, ÷òî è áûëî ñäåëàíîâ ïðèâåä¼ííîì âûøå ïðèìåðå:
    ... WHERE p.price > :price ...

    Ïîçæå ìîæíî óêàçàòü çíà÷åíèå price placeholder ÷åðåç ìåòîä setParameter():
    ->setParameter('price', '19.99')

    Èñïîëüçîâàíèå ïàðàìåòðîâ âìåñòî óñòàíîâêè çíà÷åíèé íåïîñðåäñòâåííî â ñòðîêó
    çàïðîñà ïðåäîòâðàùàåò àòàêè ÷åðåç SQL èíúåêöèè è äîëæíî èñïîëüçîâàòüñÿ âñåãäà. Ïðè èñïîëüçîâàíèè íåñêîëüêèõ ïàðàìåòðîâ, ìîæíî óêàçàòü èõ çà îäèí ðàç
    âîñïîëüçîâàâøèñü ìåòîäîì setParameters():
    ->setParameters(array(
    'price' => '19.99',
    'name' => 'Foo',
    ))

    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    133

    Symfony Documentation, Âûïóñê 2.0

    Èñïîëüçîâàíèå Doctrine's Query Builder (Êîíñòðóêòîð çàïðîñîâ Doctrine)

    Âìåñòî íåïîñðåäñòâåííîãî íàïèñàíèÿ çàïðîñîâ, ìîæíî òàêæå èñïîëüçîâàòü Doctrine
    QueryBuilder ÷òîáû ñäåëàòü òó æå ðàáîòó èñïîëüçóÿ ñèìïàòè÷íûé, îáúåêòîðèåíòèðîâàííûé èíòåðôåéñ. Åñëè èñïîëüçóåòñÿ IDE, òî ìîæíî òàêæå ïîëó÷èòü ïðåèìóùåñòâî îò àâòî-ïîäñòàíîâêè êîãäà áóäóò ââîäèòüñÿ èìåíà ìåòîäîâ. Âíóòðè êîíòðîëëåðà:
    $repository = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product');
    $query = $repository->createQueryBuilder('p')
    ->where('p.price > :price')
    ->setParameter('price', '19.99')
    ->orderBy('p.price', 'ASC')
    ->getQuery();
    $products = $query->getResult();

    Îáúåêò QueryBuilder ñîäåðæèò âñå íåîáõîäèìûå ìåòîäû äëÿ ñîçäàíèÿ çàïðîñà. Âûçâàâ
    ìåòîä getQuery(), êîíñòðóêòîð çàïðîñîâ âåðí¼ò íîðìàëüíûé îáúåêò Query, ÿâëÿþùèéñÿ òàêèì æå îáúåêòîì, êàêîé ñîçäàâàëñÿ â ïðåäûäóùåì ðàçäåëå.
    Çà äîïîëíèòåëüíîé èíôîðìàöèåé î Doctrine's Query Builder, îáðàùàéòåñü ê äîêóìåíòàöèè Query Builder.
    Custom Repository Classes

     ïðåäûäóùèõ ðàçäåëàõ âû íà÷àëè ñîçäàâàòü è èñïîëüçîâàòü áîëåå ñëîæíûå çàïðîñû
    èçíóòðè êîíòðîëëåðà. ×òîáû èçîëèðîâàòü, òåñòèðîâàòü è ïîâòîðíî èñïîëüçîâàòü èõ,
    õîðîøèì òîíîì áóäåò ñîçäàòü custom repository class äëÿ ñóùíîñòè è äîáàâèòü â íåãî
    ìåòîäû ñ çàïðîñàìè.
    ×òîáû ñäåëàòü ýòî äîáàâüòå èìÿ ðåïîçèòîðíîãî êëàññà â îòáðàæåíèå.
    ˆ

    Annotations

    // src/Acme/StoreBundle/Entity/Product.php
    namespace Acme\StoreBundle\Entity;
    use Doctrine\ORM\Mapping as ORM;
    /**
    * @ORM\Entity(repositoryClass="Acme\StoreBundle\Repository\ProductRepository")
    */
    class Product
    {

    134

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    }

    ˆ

    //...

    YAML

    # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
    Acme\StoreBundle\Entity\Product:
    type: entity
    repositoryClass: Acme\StoreBundle\Repository\ProductRepository
    # ...

    ˆ

    XML




    repository-class="Acme\StoreBundle\Repository\ProductRepository">




    Doctrine ìîæåò ñîçäàòü ðåïîçèòîðíûé êëàññ ñ ïîìîùüþ êîìàíäû, èñïîëüçîâàííîé ðàíåå
    äëÿ ñîçäàíèÿ ïðîïóùåííûõ getter è setter ìåòîäîâ:
    php app/console doctrine:generate:entities Acme

    Çàòåì äîáàâüòå íîâûé ìåòîä - findAllOrderedByName() - ê òîëüêî ÷òî ñîçäàííîìó ðåïîçèòîðîíîìó êëàññó. Îí áóäåò çàïðàøèâàòü âñå ñóùíîñòè Product, ñîðòèðîâàííûå â
    àëôàâèòíîì ïîðÿäêå.
    // src/Acme/StoreBundle/Repository/ProductRepository.php
    namespace Acme\StoreBundle\Repository;
    use Doctrine\ORM\EntityRepository;
    class ProductRepository extends EntityRepository
    {
    public function findAllOrderedByName()
    {
    return $this->getEntityManager()
    ->createQuery('SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC')
    ->getResult();
    }
    }

    Ñîâåò: Ìåíåäæåð ñóùíîñòåé äîñòóïåí ÷åðåç $this->getEntityManager() âíóòðè ðå2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    135

    Symfony Documentation, Âûïóñê 2.0

    ïîçèòîðèÿ.
    Ìîæåòå èñïîëüçîâàòü ýòîò íîâûé ìåòîä êàê è ðàíåå äîñòóïíûå ïî óìîë÷àíèþ ïîèñêîâûå
    ìåòîäû ðåïîçèòîðèÿ:
    $em = $this->getDoctrine()->getEntityManager();
    $products = $em->getRepository('AcmeStoreBundle:Product')
    ->findAllOrderedByName();

    Ïðèìå÷àíèå: Êîãäà èñïîëüçóåòñÿ custom repository class, âñ¼ åù¼ åñòü äîñòóï ê òàêèì
    ïîèñêîâûì ìåòîäàì êàê find() è findAll().

    2.8.3 Ñâÿçè/îáúåäèíåíèÿ ñóùíîñòåé

    Ïðåäïîëîæèì ÷òî âñå ïðîäóêòû â ïðèëîæåíèè ïðèíàäëåæàò åäèíñòâåííîé êàòåãîðèè.
     ýòîì ñëó÷àå, íåîáõîäèì îáúåêò Category è ñïîñîá ñâÿçûâàíèÿ åãî ñ îáúåêòîì Product.
    Íà÷í¼ì ñ ñîàçäàíèÿ ñóùíîñòè Category. Òàê êàê èçâåñòíî ÷òî â êîíå÷íîì ñ÷¼òå ïîíàäîáèòñÿ ñîõðàíèòü êëàññ ñ ïîìîùüþ Doctrine, òî ìîæíî ïîçâîëèòü Doctrine ñîçäàòü åãî
    äëÿ âàñ.

    php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Category" --fields="name:stri

    Ýòî çàäàíèå ñîçäàñò ñóùíîñòü Category ñ ïîëÿìè id, name è ñâÿçàííûìè getter è setter
    ôóíêöèÿìè.
    Ìåòàäàííûå îòîáðàæåíèÿ ñâÿçåé

    ×òîáû ñâÿçàòü ñóùíîñòè Category è Product, íà÷íèòå ñ ñîçäàíèÿ ñâîéñòâà products â
    êëàññå Category:
    // src/Acme/StoreBundle/Entity/Category.php
    // ...
    use Doctrine\Common\Collections\ArrayCollection;
    class Category
    {
    // ...
    /**
    * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
    */
    protected $products;

    136

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    }

    public function __construct()
    {
    $this->products = new ArrayCollection();
    }

    Âî-ïåðâûõ, ò. ê. îáúåêò Category ñâÿçàí ñî ìíîæåñòâîì îáúåêòîâ Product, òî äîáàâëåííîå ñâîéñòâî products áóäåò ìàññèâîì äëÿ õðàíåíèÿ îáúåêòîâ Product. Äàëåå, this
    isn't done because Doctrine needs it, but instead because it makes sense in the application
    for each Category to hold an array of Product objects.

    Ïðèìå÷àíèå: Êîä â ìåòîäå __construct() âàæåí, ïîòîìó ÷òî Doctrine íåîáõîäèìî

    ÷òîáû ñâîéñòâî $products áûëî îáúåêòîì ArrayCollection. Ýòîò îáúåêò âûãëÿäèò è
    ðàáîòàåò ïî÷òè òàêæå êàê ìàññèâ, íî èìååò ðàñøèðåííóþ ãèáêîñòü. Åñëè ýòî çàñòàâëÿåò âàñ ÷óâñòâîâàòü íåóäîáñòâî, òî íå ïåðåæèâàéòå. Ïðåäñòàâüòå ÷òî ýòî ïðîñòî ìàññèâ
    è âû áóäåòå ñíîâà â õîðîøåé ôîðìå.

    Äàëåå, ò. ê. êàæäûé êëàññ Product ìîæåò ñâÿçûâàòüñÿ òîëüêî ñ îäíèì îáúåêòîì
    Category, íåîáõîäèìî äîáàâèòü ñâîéñòâî $category ê êëàññó Product:
    // src/Acme/StoreBundle/Entity/Product.php
    // ...
    class Product
    {
    // ...

    }

    /**
    * @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
    * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
    */
    protected $category;

    Íàêîíåö, êîãäà äîáàâëåíû íîâûå ñâîéñòâà ê îáîèì êëàññàì Category è Product, ñîîáùèòå Doctrine ÷òî íàäî ñîçäàòü îòñóòñòâóþùèå ìåòîäû getter è setter:
    php app/console doctrine:generate:entities Acme

    Çàáóäüòå î ìåòàäàííûõ Doctrine íà ñåêóíäó. Èìååòñÿ äâà êëàññà - Category è Product
    with a natural one-to-many relationship. Êëàññ Category holds ìàññèâ îáúåêòîâ Product
    è îáúåêò Product ìîæåò hold îäèí îáúåêò Category. Äðóãèìè ñëîâàìè - êëàññû ïîñòðîåíû òàêèì ñïîñîáîì, êîòîðûé èìååò ñìûñë äëÿ âàøåé çàäà÷è. À òîò ôàêò, ÷òî äàííûå
    äîëæíû áûòü ñîõðàíåíû â áàçó äàííûõ, âñåãäà âòîðîñòåïåíåí.
    Òåïåðü âçãëÿíèòå íà ìåòàäàííûå íàä ñâîéñòâîì $category â êëàññå Product. Ýòà èíôîðìàöèÿ ñîîáùàåò doctrine ÷òî ñâÿçàííûì êëàññîì ÿâëÿåòñÿ Category è ÷òî îí äîëæåí
    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    137

    Symfony Documentation, Âûïóñê 2.0

    õðàíèòü id îò çàïèñè êàòåãîðèè â ïîëå category_id, íàõîäÿùåìñÿ â òàáëèöå product.
    Äðóãèìè ñëîâàìè, ñâÿçàííûé îáúåêò Category áóäåò õðàíèòñÿ â ñâîéñòâå $category,
    íî, çà êóëèñàìè, Doctrine áóäåò õðàíèòü ýòó ñâÿçü, çàïèñûâàÿ çíà÷åíèå id êàòåãîðèè â
    ñòîëáåö category_id òàáëèöû product.

    Ìåòàäàííûå íàä ñâîéñòâîì $products îáúåêòà Category ìåíåå âàæíû è ïîïðîñòó ñîîáùàþò Doctrine ÷òî íóæíî ïîñìîòðåòü ñâîéñòâî Product.category ÷òîáû âû÷èñëèòü
    êàê îòîáðàæàåòñÿ ñâÿçü.
    Ïåðåä òåì êàê ïðîäîëæèòü, óáåäèòåñü ÷òî ñîîáùèëè Doctrine äîáàâèòü íîâûå òàáëèöó
    category è ñòîëáåö product.category_id, à òàêæå íîâûé âíåøíèé êëþ÷:
    php app/console doctrine:schema:update --force

    138

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Ýòà çàäà÷à äîëæíà âûïîëíÿòüñÿ òîëüêî âî âðåìÿ ðàçðàáîòêè. Áîëåå

    íàä¼æíûé ñïîñîá ñèñòåìàòè÷åñêèõ îáíîâëåíèé ïðîèçâîäñòâåííîé áàçû äàííûõ îïèñàí
    â Ìèãðàöèÿõ Doctrine.

    Ñîõðàíåíèå ñâÿçàííûõ ñóùíîñòåé

    Òåïåðü äàâàéòå ïîñìîòðèì êîä â äåéñòâèè. Ïðåäñòàâüòå, ÷òî âû âíóòðè êîíòðîëëåðà:
    // ...
    use Acme\StoreBundle\Entity\Category;
    use Acme\StoreBundle\Entity\Product;
    use Symfony\Component\HttpFoundation\Response;
    // ...
    class DefaultController extends Controller
    {
    public function createProductAction()
    {
    $category = new Category();
    $category->setName('Main Products');
    $product = new Product();
    $product->setName('Foo');
    $product->setPrice(19.99);
    // Ñâÿçûâàåò ýòîò ïðîäóêò ñ êàòåãîðèåé
    $product->setCategory($category);
    $em = $this->getDoctrine()->getEntityManager();
    $em->persist($category);
    $em->persist($product);
    $em->flush();

    }

    return new Response(
    'Created product id: '.$product->getId().' and category id: '.$category->getId()
    );

    }

    äîáàâëåíà â òàáëèöû category è product. Â ñòîëáåö
    product.category_id äëÿ íîâîãî ïðîäóêòà óñòàíîâëåí òîò id, êîòîðûé ñîîòâåñòâóåò
    íîâîé êàòåãîðèè. Doctrine îñóùåñòâëÿåò ñîõðàíåíèå ýòîé ñâÿçè äëÿ âàñ.

    Èòàê,

    2.8.

    îäíà

    ñòðîêà

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    139

    Symfony Documentation, Âûïóñê 2.0

    Ïîëó÷åíèå ñâÿçàííûõ îáúåêòîâ

    Êîãäà íåîáõîäèìî ïîëó÷èòü îáúåäèí¼ííûå îáúåêòû, ðàáî÷èé ïðîöåññ âûãëÿäèò òàêæå êàê è ðàíüøå. Ñíà÷àëà ïîëó÷àåòå îáúåêò $product, à çàòåì äîñòóï ê ñâÿçàííîé
    Category:
    public function showAction($id)
    {
    $product = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product')
    ->find($id);
    $categoryName = $product->getCategory()->getName();
    }

    // ...

     ýòîì ïðèìåðå, ñíà÷àëà çàïðàøèâàåòñÿ îáúåêò Product ïî id ïðîäóêòà. Ýòîò çàïðîñ
    âûäà¼ò îòâåò òîëüêî äëÿ äàííûõ î ïðîäóêòå è ãèäðàòèðóåò (hydrate) îáúåêò $product
    ñ ýòèìè äàííûìè. Çàòåì, êîãäà âûçîâåòñÿ $product->getCategory()->getName(),
    Doctrine áåç ëèøíåãî øóìà ñäåëàåò âòîðîé çàïðîñ, ÷òîáû íàéòè Category, êîòîðàÿ ñâÿçàíà ñ ýòèì Product. Îíà ïîäãîòîâèò îáúåêò $category è âîçâðàòèò åãî âàì.

    Âàæåí òîò ôàêò, ÷òî ó âàñ åñòü ïðîñòîé äîñòóï ê êàòåãîðèè, ñâÿçàííîé ñ ïðîäóêòîì, íî
    å¼ äàííûå íå èçâëåêàþòñÿ, ïîêà îíà âàì íå ïîíàäîáèòñÿ (ò. å. ýòî ëåíèâàÿ çàãðóçêà).
    Òàêæå ìîæíî çàïðîñèòü â äðóãîì íàïðàâëåíèè:
    140

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    public function showProductAction($id)
    {
    $category = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Category')
    ->find($id);
    $products = $category->getProducts();
    }

    // ...

     ýòîì ñëó÷àå ïðîèñõîäÿò ïîõîæèå äåëà: ñíà÷àëà çàïðàøèâàåòå îäèí îáúåêò Category,
    çàòåì Doctrine äåëàåò âòîðîé çàïðîñ äëÿ ïîëó÷åíèÿ ñâÿçàííûõ îáúåêòîâ Product, íî
    òîëüêî îäíàæäû - êîãäà îíè âàì ïîíàäîáÿòñÿ (ò. å. êîãäà âûçûâàåòñÿ ->getProducts()).
    Ïåðåìåííàÿ $products ÿâëÿåòñÿ ìàññèâîì âñåõ îáúåêòîâ Product, ñâÿçàííûõ ñ äàííûì
    îáúåêòîì Category ÷åðåç çíà÷åíèå èõ category_id.

    Ñâÿçè è proxy êëàññû
    Ýòà ëåíèâàÿ çàãðóçêà âîçìîæíà, êîãäà íåîáõîäèìà, ïîòîìó, ÷òî Doctrine âîçâðàùàåò proxy îáúåêò âìåñòî íàñòîÿùåãî îáúåêòà. Âçãëÿíèòå ñíîâà íà ïðèìåð, ïðèâåä¼ííûé ðàíåå:
    $product = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product')
    ->find($id);
    $category = $product->getCategory();
    // prints "Proxies\AcmeStoreBundleEntityCategoryProxy"
    echo get_class($category);

    Ýòîò proxy îáúåêò ðàñøèðÿåò íàñòîÿùèé îáúåêò Category, è âûãëÿäèò è äåéñòâóåò
    òàê æå êàê è îí. Îòëè÷èå ëèøü â òîì, ÷òî èñïîëüçóÿ proxy îáúåêò, Doctrine ìîæåò
    îòëîæèòü çàïðîñ äåéñòâèòåëüíûõ äàííûõ î Category ïîêà îíè âàì íå ïîíàäîáÿòñÿ
    (ò. å. ïîêà íå âûçîâåòå $category->getName()).
    Proxy êëàññû ñîçäàþòñÿ Doctrine è õðàíÿòñÿ â ïàïêå cache. È õîòÿ âàì, âåðîÿòíî,
    íèêîãäà íå ïðèä¼òñÿ ïðèíèìàòü âî âíèìàíèå ÷òî îáúåêò $category íà ñàìîì äåëå
    ÿâëÿåòñÿ proxy îáúåêòîì, íî âàæíî çíàòü îá ýòîì.
     ñëåäóþùåì ðàçäåëå áóäåì ïîëó÷àòü äàííûå î ïðîäóêòå è êàòåãîðèè çà îäèí çàõîä
    (÷åðåç join ), à Doctrine áóäåò âîçâðàùàòü íàñòîÿùèé îáúåêò Category, ò. ê. íå áóäåò
    íóæäû â ëåíèâîé çàãðóçêå.

    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    141

    Symfony Documentation, Âûïóñê 2.0

    Îáúåäèíåíèå ñî ñâÿçàííûìè çàïèñÿìè

    Â ïðåäûäóùèõ ïðèìåðàõ âûïîëíÿëîñü ïî äâà çàïðîñà - îäèí äëÿ èñõîäíîãî îáúåêòà
    (íàïðèìåð, Category) è îäèí äëÿ ñâÿçàííîãî (íàïðèìåð, îáúåêòû Product).

    Ñîâåò: Âñïîìíèòå, ÷òî ìîæíî óâèäåòü âñå çàïðîñû ê áàçå äàííûõ, ñäåëàííûå âî âðåìÿ
    âåá-çàïðîñà, ÷åðåç ïàíåëü èíñòðóìåíòîâ web debug.

    Êîíå÷íî, åñëè çàðàíåå èçâåñòíî ÷òî áóäåò íåîáõîäèì äîñòóï ê îáîèì îáúåêòàì, òî ìîæíî
    èçáåæàòü âòîðîãî çàïðîñà, èñïîëüçóÿ join â èñõîäíîì çàïðîñå. Äîáàâüòå ñëåäóþùèé
    ìåòîä ê êëàññó ProductRepository:
    // src/Acme/StoreBundle/Repository/ProductRepository.php
    public function findOneByIdJoinedToCategory($id)
    {
    $query = $this->getEntityManager()
    ->createQuery('
    SELECT p, c FROM AcmeStoreBundle:Product p
    JOIN p.category c
    WHERE p.id = :id'
    )->setParameter('id', $id);

    }

    try {
    return $query->getSingleResult();
    } catch (\Doctrine\ORM\NoResultException $e) {
    return null;
    }

    Òåïåðü ìîæåòå èñïîëüçîâàòü ýòîò ìåòîä â êîíòðîëëåðå ÷òîáû ïîëó÷àòü îáúåêò Product
    è ñâÿçàííóþ Category çà îäèí çàïðîñ:
    public function showAction($id)
    {
    $product = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product')
    ->findOneByIdJoinedToCategory($id);
    $category = $product->getCategory();
    }

    142

    // ...

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïîäðîáíåå îá îáúåäèíåíèÿõ

    Ýòîò ðàçäåë ÿâëÿåòñÿ ââåäåíèåì ê îäíîìó îáùåìó òèïó ñâÿçè ñóùíîñòåé - ñâÿçè îäèíêî-ìíîãèì. Çà áîëåå ïðîäâèíóòûìè ïîäðîáíîñòÿìè è ïðèìåðàìè èñïîëüçîâàíèÿ äðóãèõ
    òèïîâ ñâÿçåé (íàïð., îäèí-ê-îäíîìó, ìíîãèå-êî-ìíîãèì), îáðàùàéòåñü ê Îòîáðàæåíèÿì
    îáúåäèíåíèé äëÿ Doctrine.

    Ïðèìå÷àíèå: Åñëè èñïîëüçîâàòü àííîòàöèè, íåîáõîäèìî ïðåäâàðÿòü èõ óïîìèíàíè-

    ÿìè îá ORM\ (íàïð., ORM\OneToMany), ïðî ýòî íå ãîâîðèòñÿ â äîêóìåíòàöèè Doctrine.
    Òàêæå íåîáõîäèìî âêëþ÷èòü âûðàæåíèå use Doctrine\ORM\Mapping as ORM;, êîòîðîå
    âíåäðÿåò ïðåôèêñ àííîòàöèè ORM.

    2.8.4 Êîíôèãóðàöèÿ

    Doctrine î÷åíü ãèáêà, õîòÿ âàì, âåðîÿòíî, íèêîãäà íå ïðèä¼òüñÿ áåñïîêîèòüñÿ î áîëüøåé
    ÷àñòè å¼ îïöèé. ×òîáû óçíàòü áîëüøå î íàñòðîéêå Doctrine, see the Doctrine section of
    the reference manual.
    2.8.5 Lifecycle Callbacks

    Èíîãäà òðåáóåòñÿ âûïîëíèòü äåéñòâèÿ ñðàçó æå ïåðåä èëè ïîñëå òîãî êàê ñóùíîñòü áóäåò âñòàâëåíà, îáíîâëåíà èëè æå óäàëåíà. Òàêèå òèïû äåéñòâèé èçâåñòíû êàê lifecycle
    callbacks, ò. ê. îíè âûçûâàþò ìåòîäû, êîòîðûå íåîáõîäèìî âûïîëíèòü âî âðåìÿ ðàçëè÷íûõ ñòàäèé æèçíåííîãî öèêëà ñóùíîñòè (íàïð., ñóùíîñòü âñòàâëåíà, îáíîâëåíà, óäàëåíà
    è ò. ä.).
    Åñëè äëÿ ìåòàäàííûõ âû èñïîëüçóåòå àííîòàöèè, òî íà÷íèòå ñ âêëþ÷åíèÿ lifecycle
    callbacks. Â ýòîì íåò íåîáõîäèìîñòè åñëè äëÿ îòîáðàæåíèé èñïîëüçóþòñÿ YAML èëè
    XML:
    /**
    * @ORM\Entity()
    * @ORM\HasLifecycleCallbacks()
    */
    class Product
    {
    // ...
    }

    Òåïåðü ìîæíî äàòü çàäàíèå Doctrine âûïîëíèòü ìåòîä äëÿ ëþáîãî äîñòóïíîãî ñîáûòèÿ
    æèçíåííîãî öèêëà. Íàïðèìåð, íàäî óñòàíîâèòü òåêóùóþ äàòó â êîëîíêó created òîëüêî
    âî âðåìÿ ïåðâîãî ñîõðàíåíèÿ ñóùíîñòè (ò. å. âî âðåìÿ âñòàâêè):
    ˆ
    2.8.

    Annotations

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    143

    Symfony Documentation, Âûïóñê 2.0

    /**
    * @ORM\prePersist
    */
    public function setCreatedValue()
    {
    $this->created = new \DateTime();
    }

    ˆ

    YAML

    # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
    Acme\StoreBundle\Entity\Product:
    type: entity
    # ...
    lifecycleCallbacks:
    prePersist: [ setCreatedValue ]

    ˆ

    XML












    Ïðèìå÷àíèå: Ïðåäûäóùèå ïðèìåðû ïðåäïîëàãàþò ÷òî ñâîéñòâî created óæå ñîçäàíî

    è îòîáðàæåíî (çäåñü ýòî íå áûëî ïîêàçàíî).

    Ñðàçó æå ïåðåä ïåðâûì ñîõðàíåíèåì ñóùíîñòè, Doctrine àâòîìàòè÷åñêè âûçîâåò ýòîò
    ìåòîä è â ïîëå created áóäåò óñòàíîâëåíà òåêóùàÿ äàòà.
    Òî æå ñàìîå ìîæíî ïðîäåëàòü äëÿ ëþáîãî äðóãîãî ñîáûòèÿ æèçíåííîãî öèêëà, ñðåäè
    êîòîðûõ:
    ˆ preRemove
    ˆ postRemove
    ˆ prePersist
    ˆ postPersist
    ˆ preUpdate
    144

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ postUpdate
    ˆ postLoad
    ˆ loadClassMetadata
    Äîïîëíèòåëüíàÿ èíôîðìàöèÿ î òîì, ÷òî èç ñåáÿ ïðåäñòàâëÿþò ýòè ñîáûòèÿ è âûçîâû
    âíóòðè æèçíåííîãî öèêëà â îáùåì âèäå, íàõîäèòñÿ â Äîêóìåíòàöèè ïî Lifecycle Events

    Lifecycle Callbacks è Event Listeners
    Îáðàòèòå âíèìàíèå ÷òî ìåòîä setCreatedValue() íå ïîëó÷àåò àðãóìåíòîâ. Ýòî
    íåîáõîäèìîñòü äëÿ lifecylce callbacks è ýòî ñäåëàíî ïðåäíàìåðåííî: lifecycle callbacks
    äîëæíû áûòü ïðîñòûìè ìåòîäàìè, çàíèìàþùèìèñÿ âíóòðåííèìè èçìåíåíèÿìè
    äàííûõ äëÿ ñóùíîñòè (íàïð., óñòàíîâêà çíà÷åíèé äëÿ ïîëåé created/updated, ñîçäàíèå slug).
    Åñëè ïëàíèðóåòñÿ äåëàòü áîëåå òÿæ¼ëóþ ðàáîòó - çàïèñü ëîãîâ èëè îòïðàâêà email
    - íåîáõîäèìî çàðåãèñòðèðîâàòü âíåøíèé êëàññ êàê event listener èëè subscriber è
    äàòü åìó äîñòóï ê íåîáõîäèìûì ðåñóðñàì. Äîïîëíèòåëüíóþ èíôîðìàöèþ íàéä¼òå
    â /cookbook/doctrine/event_listeners_subscribers.

    2.8.6 Ðàñøèðåíèÿ äëÿ Doctrine: Timestampable, Sluggable è äðóãèå

    Doctrine ðàñøèðÿåìà, ïîýòîìó äîñòóïíî ìíîæåñòâî ñòîðîííèõ ðåøåíèé, ïîçâîëÿþùèõ ñ
    ë¼ãêîñòüþ âûïîëíÿòü ïîâòîðÿþùèåñÿ è îáùèå çàäà÷è íàä ñóùíîñòÿìè. Ñðåäè íèõ åñòü
    ñëåäóþùèå: Sluggable, Timestampable, Loggable, Translatable è Tree.
    Ïîäðîáíåå î òîì ãäå íàéòè è êàê èñïîëüçâàòü ýòè ðàñøèðåíèÿ ðàñêàçûâàåò ñòàòüÿ
    Èñïîëüçîâàíèå îáùèõ ðàñøèðåíèé Doctrine.
    2.8.7 Ñïðàâêà ïî òèïàì ïîëåé â Doctrine

    Doctrine ïðåäñòàâëÿåò îãðîìíîå êîëè÷åñòâî òèïîâ ïîëåé. Êàæäûé èç êîòîðûõ îòîáðàæàåò òèï äàííûõ èç PHP â óñòàíîâëåííûé òèï êîëîíêè äëÿ ëþáîé èñïîëüçóåìîé áàçû
    äàííûõ. Â Doctrine ïîääåðæèâàþòñÿ ñëåäóþùèå òèïû:
    ˆ Ñòðîêè

     string (èñïîëüçóåòñÿ äëÿ êîðîòêèõ ñòðîê)
     text (èñïîëüçóåòñÿ äëÿ äëèííûõ ñòðîê)
    ˆ ×èñëà

     integer
    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    145

    Symfony Documentation, Âûïóñê 2.0

     smallint
     bigint
     decimal
     float
    ˆ Äàòà è âðåìÿ (èñïîëüçóéòå îáúåêò DateTime â PHP äëÿ ýòèõ ïîëåé)

     date
     time
     datetime
    ˆ Äðóãèå òèïû

     boolean
     object (ñåðèàëèçóåòñÿ è õðàíèòñÿ â ïîëå CLOB)
     array (ñåðèàëèçóåòñÿ è õðàíèòñÿ â ïîëå CLOB)
    Äîïîëíèòåëüíàÿ èíôîðìàöèÿ ñîäåðæèòñÿ â Îòîáðàæåíèè òèïîâ.
    Îïöèè ïîëåé

    Êàæäîå ïîëå ìîæåò èìåòü íàáîð îïöèé, ïðèìåíèìûõ ê íåìó. Äîñòóïíûå îïöèè âêëþ÷àþò: type (ñòàíäàðòíûé äëÿ string), name, length, unique è nullable. Íåñêîëüêî
    ïðèìåðîâ òàêèõ àííîòàöèé:
    /**
    * Ñòðîêîâîå ïîëå äëèíîé 255, êîòîðîå íå äîëæíî áûòü null
    * (ýòî ñòàíäàðòíûå çíà÷åíèÿ äëÿ îïöèé "type", "length" è *nullable*)
    *
    * @ORM\Column()
    */
    protected $name;
    /**
    * Ñòðîêîâîå ïîëå äëèíîé 150, õðàíÿùååñÿ â êîëîíêå "email_address"
    * è èìåþùåå óíèêàëüíûé èíäåêñ.
    *
    * @ORM\Column(name="email_address", unique="true", length="150")
    */
    protected $email;

    Ïðèìå÷àíèå: Ñóùåñòâóþò åù¼ îïöèè, î êîòîðûõ çäåñü íå óïîìèíàåòñÿ. Çà äîïîë-

    íèòåëüíîé èíôîðìàöèåé îáðàùàéòåñü ê äîêóìåíòàöèè Doctrine's Property Mapping
    documentation
    146

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    2.8.8 Êîíñîëüíûå êîìàíäû

    Èíòåãðàöèÿ Doctrine2 ORM ïðåäëàãàåò íåñêîëüêî êîíñîëüíûõ êîìàíä âíóòðè ïðîñòðàíñòâà èì¼í doctrine. ×òîáû âûâåñòè ñïèñîê êîìàíä çàïóñòèòå êîíñîëü áåç àðãóìåíòîâ:
    php app/console

     âûâåäåííîì ñïèñêå äîñòóïíûõ êîìàíä ìíîãèå èç íèõ íà÷èíàþòñÿ ñ ïðåôèêñà
    doctrine:. Ïîäðîáíåå î íèõ (èëè ëþáûõ äðóãèõ êîìàíäàõ äëÿ Symfony) ìîæíî
    óçíàòü çàïóñòèâ êîìàíäó help. Íàïðèìåð, ÷òîáû ïîëó÷èòü ïîäðîáíîñòè î ïðîöåññå
    doctrine:database:create, çàïóñòèòå:
    php app/console help doctrine:database:create

    Íåêîòîðûå èíòåðåñíûå èëè ïðèìå÷àòåëüíûå êîìàíäû âêëþ÷àþò:
    ˆ doctrine:ensure-production-settings - ïðîâåðÿåò òåêóùåå îêðóæåíèå, íàñòðîåíî ëè îíî ýôôåêòèâíî äëÿ ïðîèçâîäñòâåííûõ íóæä. Îíà âñåãäà äîëæíà çàïóñêàòüñÿ â îêðóæåíèè prod:
    php app/console doctrine:ensure-production-settings --env=prod

    ˆ doctrine:mapping:import - ðàçðåøàåò Doctrine ïðîàíàëèçèðîâàòü ñóùåñòâóþùóþ
    áàçó äàííûõ è ñîçäàòü èíôîðìàöèþ äëÿ å¼ îòîáðàæåíèÿ. Çà äîïîëíèòåëüíîé èíôîðìàöèåé îáðàùàéòåñü ê /cookbook/doctrine/reverse_engineering.
    ˆ doctrine:mapping:info - ðàññêàæåò îáî âñåõ ñóùíîñòÿõ, êîòîðûå çíàåò Doctrine,
    à òàêæå åñòü ëè â îòîáðàæåíèÿõ êàêèå-íèáóäü ïðîñòûå îøèáêè.
    ˆ doctrine:query:dql è doctrine:query:sql - ïîçâîëÿåò âûïîëíÿòü DQL èëè SQL
    çàïðîñû ïðÿìî èç êîìàíäíîé ñòðîêè.

    Ïðèìå÷àíèå: ×òîáû èìåòü âîçìîæíîñòü çàãðóæàòü xtures ñ äàííûìè â áàçó äàííûõ,
    íåîáõîäèìî óñòàíîâèòü áàíäë DoctrineFixturesBundle. ×òîáû óçíàòü êàê ýòî ñäåëàòü,
    ïðî÷òèòå ñòàòüþ  /bundles/DoctrineFixturesBundle/index â äîêóìåíòàöèè.

    2.8.9 Âûâîäû

    Ïðèìåíÿÿ Doctrine, ìîæíî ñôîêóñèðîâàòüñÿ íà îáúåêòàõ è èõ èñïîëüçîâàíèè â ïðèëîæåíèè è òîëüêî ïîòîì çàáîòèòüñÿ îá èõ ñîõðàíåíèè â áàçó äàííûõ. Áëàãîäàðÿ òîìó, ÷òî
    Doctrine ïîçâîëÿåò èñïîëüçîâàòü ëþáîé îáúåêò PHP äëÿ õðàíåíèÿ äàííûõ è ïðèìåíÿåò
    èíôîðìàöèþ ìåòàäàííûõ äëÿ îòîáðàæåíèÿ ÷òîáû îòîáðàçèòü ýòè äàííûå îá îáúåêòå â
    îïðåäåë¼ííóþ òàáëèöó áàçû äàííûõ.
    2.8.

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    147

    Symfony Documentation, Âûïóñê 2.0

    Õîòÿ â îñíîâå Doctrine ïðîñòàÿ èäåÿ, îíà íåîáû÷àéíî ìîùíà, ïîçâîëÿåò ñîçäàâàòü ñëîæíûå çàïðîñû è ïîäïèñûâàòüñÿ íà ñîáûòèÿ, êîòîðûå äàþò âîçìîæíîñòü ñîâåðøàòü ðàçëè÷íûå äåéñòâèÿ êîãäà îáúåêòû ïðîõîäÿò ïî ñâîèì æèçíåííûì öèêëàì âî âðåìÿ ñîõðàíåíèÿ.
    Çà äîïîëíèòåëüíîé èíôîðìàöèåé î Doctrine îáðàùàéòåñü ê ðàçäåëó
    ðåöåïòîâ , êîòîðûé âêëþ÷àåò ñëåäóþùèå ñòàòüè:

    Doctrine

    èç

    Êíèãè

    ˆ /bundles/DoctrineFixturesBundle/index
    ˆ /cookbook/doctrine/common_extensions

    2.9 Òåñòèðîâàíèå
    Êàê òîëüêî âû ïèøåòå íîâóþ ñòðîêó êîäà, âû òàêæå ïîòåíöèàëüíî äîáàâëÿåòå íîâûå
    îøèáêè. Äëÿ òîãî ÷òîáû ñîçäàâàòü íàä¼æíûå ïðèëîæåíèÿ, âû äîëæíû èñïîëüçîâàòü
    êàê ôóíêöèîíàëüíûå, òàê è ìîäóëüíûå (unit) òåñòû.
    2.9.1 Òåñòîâûé ôðåéìâîðê PHPUnit

    Â Symfony2 èíòåãðèðîâàíà ïîääåðæêà íåçàâèñèìîé áèáëèîòåêè - íàçûâàåìîé PHPUnit
    - ÷òîáû ïðåäîñòàâèòü âàì îòëè÷íûé òåñòîâûé ôðåéìâîðê. Ýòà ãëàâà íå ïîêðûâàåò âñå
    íþàíñû PHPUnit, òàê êàê âû âñåãäà ìîæåòå ïî÷èòàòü åãî ïîäðîáíóþ äîêóìåíòàöèþ.

    Ïðèìå÷àíèå: Symfony2 ðàáîòàåò ñ PHPUnit 3.5.11 èëè ñòàðøå.
    Êàæäûé òåñò - âíå çàâèñèìîñòè îò òîãî ôóíêöèîíàëüíûé îí èëè ìîäóëüíûé - ýòî PHP
    êëàññ, êîòîðûé ðàñïîëîæåí â ïîääèðåêòîðèè Tests/ âàøèã ïàêåòîâ. Åñëè âû áóäåòå
    ñëåäîâàòü ýòîìó ïðàâèëó, òî âû ñìîæåòå çàïóñêàòü âñå òåñòû âàøåãî ïðèëîæåíèÿ ïðè
    ïîìîùè êîìàíäû:
    # óêàæèòå ïàïêó ñ êîíôèãàìè â êîìàíäíîé ñòðîêå
    $ phpunit -c app/

    Îïöèÿ -c óêàçûâàåò PHPUnit èñêàòü êîíôèãóðàöèîííûé ôàéë â äèðåêòîðèè
    app/. Åñëè âû èíòåðåñóåòåñü îïöèÿìè PHPUnit, îáðàòèòå âíèìàíèå íà ôàéë
    app/phpunit.xml.dist.

    Ñîâåò: Ïîêðûòèå êîäà ìîæåò áûòü ïîëó÷åíî ñ ïîìîùüþ îïöèè --coverage-html.

    148

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    2.9.2 Ìîäóëüíûå òåñòû

    Ìîäóëüíûé òåñò - ýòî êàê ïðàâèëî òåñò îäíîãî îòäåëüíîãî PHP êëàññà. Åñëè âû õîòèòå
    òåñòèðîâàòü ïîâåäåíèå âàøåãî ïðèëîæåíèÿ öåëèêîì, îáðàòèòåñü ê ñåêöèè Ôóíêöèîíàëüíûå òåñòû.
    Íàïèñàíèå ìîäóëüíûõ òåñòîâ â Symfony2 íå îòëè÷àåòñÿ îò íàïèñàíèÿ ñòàíäàðòíûõ ìîäóëüíûõ òåñòîâ PHPUnit. Íàïðèìåð, ïðåäïîëîæèì, ó âàñ åñòü î÷åíü ïðîñòîé êëàññ
    Calculator â äèðåêòîðèè Utility/ âàøåãî ïàêåòà:
    // src/Acme/DemoBundle/Utility/Calculator.php
    namespace Acme\DemoBundle\Utility;
    class Calculator
    {
    public function add($a, $b)
    {
    return $a + $b;
    }
    }

    Äëÿ òîãî, ÷òîáû åãî ïðîòåñòèðîâàòü, ñîçäàéòå ôàéë CalculatorTest â äèðåêòîðèè
    Tests/Utility âàøåãî ïàêåòà:
    // src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
    namespace Acme\DemoBundle\Tests\Utility;
    use Acme\DemoBundle\Utility\Calculator;
    class CalculatorTest extends \PHPUnit_Framework_TestCase
    {
    public function testAdd()
    {
    $calc = new Calculator();
    $result = $calc->add(30, 12);

    }

    }

    // assert that our calculator added the numbers correctly!
    $this->assertEquals(42, $result);

    Ïðèìå÷àíèå: Ïî ñîãëàøåíèþ, ïîä-äèðåêòîðèÿ Tests/ äîëæíà ïîâòîðÿòü ñòðóêòóðó

    äèðåêòîðèé âàøåãî ïàêåòà. Ò.î. åñëè âû òåñòèðóåòå êëàññ âàøåãî ïàêåòà èç äèðåêòîðèè
    Utility/, ïîìåñòèòå òåñò â äèðåêòîðèþ Tests/Utility/.

    2.9.

    Òåñòèðîâàíèå

    149

    Symfony Documentation, Âûïóñê 2.0

    Êàê è â âàøåì ïðèëîæåíèè, àâòîçàãðóçêà âêëþ÷àåòñÿ àâòîìàòè÷åñêè ïðè ïîìîùè ôàéëà bootstrap.php.cache (ýòî ïî óìîë÷àíèþ íàñòðîåíî â ôàéëå phpunit.xml.dist).
    Âûïîëíèòü òåñòû äëÿ çàäàííîãî ôàéëà èëè ïàïêè òàêæå ïðîñòî:
    # run all tests in the Utility directory
    $ phpunit -c app src/Acme/DemoBundle/Tests/Utility/
    # run tests for the Calculator class
    $ phpunit -c app src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
    # çàïóñòèòü âñå òåñòû äëÿ öåëîãî Bundle
    $ phpunit -c app src/Acme/DemoBundle/

    2.9.3 Ôóíêöèîíàëüíûå òåñòû

    Ôóíêöèîíàëüíûå òåñòû ïðîâåðÿþò îáúåäèíåíèÿ ðàçëè÷íûõ ñëî¼â ïðèëîæåíèÿ (îò
    ìàðøðóòèçàöèè äî âèäîâ). Îíè íå îòëè÷àþòñÿ îò ìîäóëüíûõ òåñòîâ íàñòîëüêî, íàñêîëüêî PHPUnit ïîçâîëÿåò ýòî, íî èìåþò êîíêðåòíûé ðàáî÷èé ïðîöåññ:
    ˆ Ñäåëàòü çàïðîñ;
    ˆ Ïðîòåñòèðîâàòü îòâåò;
    ˆ Êëèêíóòü ïî ññûëêå èëè îòïðàâèòü ôîðìó;
    ˆ Ïðîòåñòèðîâàòü îòâåò;
    ˆ Ïðîôèëüòðîâàòü è ïîâòîðèòü.
    Âàø ïåðâûé ôóíêöèîíàëüíûé òåñò

    Ôóíêöèîíàëüíûå òåñòû - ýòî ïðîñòûå PHP êëàññû, êîòîðûå, êàê ïðàâèëî, ðàñïîëàãàþòñÿ â äèðåêòîðèè ïàêåòà Tests/Controller. Åñëè âû õîòèòå ïðîòåñòèðîâàòü ñòðàíèöû,
    êîòîðûå ñîäåðæèò âàø êëàññ DemoController, ñîçäàéòå íîâûé êëàññ, êîòîðûé ðàñøèðÿåò ñïåöèàëüíûé êëàññ WebTestCase.
    Íàïðèìåð, Symfony2 Standard Edition ïðåäîñòàâëÿåò ïðîñòîé ôóíêöèîíàëüíûé òåñò äëÿ
    åãî DemoController (DemoControllerTest), êîòîðûé âûãëÿäèò òàê:
    // src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php
    namespace Acme\DemoBundle\Tests\Controller;
    use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
    class DemoControllerTest extends WebTestCase
    {
    150

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    public function testIndex()
    {
    $client = static::createClient();
    $crawler = $client->request('GET', '/demo/hello/Fabien');

    }

    $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);

    }

    Ñîâåò: Äëÿ çàïóñêà âàøèõ ôóíêöèîíàëüíûõ òåñòîâ, êëàññ WebTestCase çàãðóæàåò ÿä-

    ðî âàøåãî ïðèëîæåíèÿ.  áîëüøèíñòâå ñëó÷àåâ, ýòî ïðîèñõîäèò àâòîìàòè÷åñêè. Òåì
    íå ìåíåå, åñëè âàøå ÿäðî íàõîäèòñÿ â íåñòàíäàðòíîé äèðåêòîðèè, âàì íóæíî ìîäèôèöèðîâàòü ôàéë phpunit.xml.dist è óñòàíîâèòü ïåðåìåííóþ ñðåäû KERNEL_DIR íà
    äèðåêòîðèþ âàøåãî ÿäðà:







    Ìåòîä createClient() âîçâðàùàåò êëèåíò, êîòîðûé íàïîìèíàåò áðàóçåð, êîòîðûé âû
    èñïîëüçóåòå äëÿ ïðîñìîòðà âàøåãî ñàéòà:
    $crawler = $client->request('GET', '/demo/hello/Fabien');

    request() (ñì. ïîäðîáíåå î ìåòîäå request ) âîçâðàùàåò îáúåêò
    Symfony\Component\DomCrawler\Crawler, êîòîðûé ìîæåò áûòü èñïîëüçîâàí äëÿ
    âûáîðà ýëåìåíòîâ â Response, êëèêîâ ïî ññûëêàì è îòïðàâêè ôîðì.
    Ìåòîä

    Ñîâåò: Crawler ìîæåò èñïîëüçîâàòüñÿ òîëüêî â òîì ñëó÷àå, åñëè ñîäåðæèìîå Response
    ýòî XML èëè HTML äîêóìåíò. Äëÿ äðóãèõ òèïîâ íóæíî ïîëó÷àòü ñîäåðæèìîå Response
    ÷åðåç $client->getResponse()->getContent().

    Äàâàéòå êëèêíåì ïî ññûëêå, âûáðàâ å¼ ïðè ïîìîùè Crawler èñïîëüçóÿ XPath èëè CSS
    ñåëåêòîð, çàòåì èñïîëüçóåì Client äëÿ ñîáñòâåííî êëèêà. Íàïðèìåð, ñëåäóþùèé êîä
    íàõîäèò âñå ññûëêè ñ òåêñòîì Greet, çàòåì âûáèðàåò âòîðóþ èç íèõ è êëèêàåò íà íå¼:
    $link = $crawler->filter('a:contains("Greet")')->eq(1)->link();
    $crawler = $client->click($link);

    2.9.

    Òåñòèðîâàíèå

    151

    Symfony Documentation, Âûïóñê 2.0

    Îòïðàâêà ôîðìû ïðîèñõîäèò ñõîæèì îáðàçîì: âûáåðèòå êíîïêó íà ôîðìå, ïî æåëàíèþ
    ïåðåîïðåäåëèòå êàêèå-íèáóäü çíà÷åíèÿ ôîðìû, è îòïðàâüòå å¼:
    $form = $crawler->selectButton('submit')->form();
    // óñòàíàâëèâàåò êàêèå-íèáóäü çíà÷åíèÿ
    $form['name'] = 'Lucas';
    $form['form_name[subject]'] = 'Hey there!';
    // îòïðàâëÿåò ôîðìó
    $crawler = $client->submit($form);

    Ñîâåò: Ôîðìà òàêæå ïîääåðæèâåò çàãðóçêó ôàéëîâ è ñîäåðæèò ìåòîäû äëÿ çàïîë-

    íåíèÿ ðàçëè÷íûõ òèïîâ ïîëåé (íàïðèìåð, select() è tick()). Ïîäðîáíåå ÷èòàéòå â
    ñåêöèè Ôîðìû íèæå.

    Òåïåðü, êîãäà âû ñ ë¼ãêîñòüþ ìîæåòå ïåðåìåùàòüñÿ ïî ïðèëîæåíèþ, âîñïîëüçóéòåñü
    óòâåðæäåíèÿìè ÷òîáû ïðîâåðèòü îæèäàåìûå äåéñòâèÿ. Âîñïîëüçóéòåñü Crawler ÷òîáû
    ñäåëàòü óòâåðæäåíèÿ äëÿ DOM:
    // Óòâåðæäàåò ÷òî îòâåò ñîîòâåñòâóåò çàäàííîìó CSS ñåëåêòîðó.
    $this->assertTrue($crawler->filter('h1')->count() > 0);

    Èëè ïðîâåðüòå ñîäåðæèìîå Response íàïðÿìóþ, åñëè õîòèòå óáåäèòüñÿ ÷òî åãî ñîäåðæèìîå âêëþ÷àåò êàêîé-òî òåêñò, èëè ÷òî Response íå ÿâëÿåòñÿ äîêóìåíòîì XML/HTML:
    $this->assertRegExp('/Hello Fabien/', $client->getResponse()->getContent());

    152

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïîäðîáíåå î ìåòîäå request():
    Ïîëíàÿ ñèãíàòóðà ìåòîäà request():
    request(
    $method,
    $uri,
    array $parameters = array(),
    array $files = array(),
    array $server = array(),
    $content = null,
    $changeHistory = true
    )

    Ìàññèâ server - ýòî çíà÷åíèÿ, êîòîðûå âû êàê ïðàâèëî îæèäàåòå íàéòè â ñóïåðãëîáàëüíîì ìàññèâå $_SERVER. Íàïðèìåð, äëÿ òîãî, ÷òîáû óñòàíîâèòü HTTP çàãîëîâêè Content-Type è Referer âû äîëæíû ïåðåäàòü ñëåäóþùåå:
    $client->request(
    'GET',
    '/demo/hello/Fabien',
    array(),
    array(),
    array(
    'CONTENT_TYPE' => 'application/json',
    'HTTP_REFERER' => '/foo/bar',
    )
    );

    Äëÿ áûñòðîãî ñòàðòà îáðàòèòå âíèìàíèå íà ñïèñîê íàèáîëå òèïîâûõ è ïîëåçíûõ óòâåðæäåíèé:
    // Óòâåðæäàåò ÷òî èìååòñÿ åäèíñòâåííûé òàã h2 ñ êëàññîì "subtitle"
    $this->assertTrue($crawler->filter('h2.subtitle')->count() > 0);
    // Óòâåðæäàåò ÷òî íà ñòðàíèöå èìååòñÿ 4 òàãà h2
    $this->assertEquals(4, $crawler->filter('h2')->count());

    // Óòâåðæäàåò ÷òî çàãîëîâîê "Content-Type" - "application/json"
    $this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/js
    // Óòâåðæäàåò ÷òî òåëî îòâåòà ñîîòâåòñòâóåò ðåãóëÿðíîìó âûðàæåíèþ
    $this->assertRegExp('/foo/', $client->getResponse()->getContent());
    // Óòâåðæäàåò ÷òî ñòàòóñ-êîä îòâåòà 2xx
    2.9.

    Òåñòèðîâàíèå

    153

    Symfony Documentation, Âûïóñê 2.0

    $this->assertTrue($client->getResponse()->isSuccessful());
    // Óòâåðæäàåò ÷òî ñòàòóñ-êîä îòâåòà 404
    $this->assertTrue($client->getResponse()->isNotFound());
    // Óòâåðæäàåò ÷òî ñòàòóñ-êîä îòâåòà òî÷íî 200
    $this->assertEquals(200, $client->getResponse()->getStatusCode());
    // Óòâåðæäàåò ÷òî îòâåò - ýòî ïåðåíàïðàâëåíèå íà /demo/contact
    $this->assertTrue($client->getResponse()->isRedirect('/demo/contact'));
    // èëè ïðîñòî ïðîâåðÿåò, ÷òî îòâåò - ýòî ïåðåíàïðàâëåíèå íà ëþáîé URL
    $this->assertTrue($client->getResponse()->isRedirect());

    2.9.4 Ðàáîòàåì ñ Òåñòîâûì êëèåíòîì

    Òåñòîâûé êëèåíò ñèìóëèðóåò HTTP êëèåíò (êàê ïðàâèëî ýòî áðàóçåð) è âûïîëíÿåò
    çàïðîñû ê âàøåìó Symfony2 ïðèëîæåíèþ:
    $crawler = $client->request('GET', '/hello/Fabien');

    Ìåòîä request() ïðèíèìàåò â êà÷åñòâå àðãóìåíòîâ HTTP ìåòîä è URL è âîçâðàùàåò
    ýêçåìïëÿð Crawler.
    Èñïîëüçóéåò Crawler äëÿ íàõîæäåíèÿ DOM-ýëåìåíòîâ â òåëå Response. Ïîñëå ýòè ýëåìåíòû ìîãóò áûòü èñïîëüçîâàíû äëÿ êëèêîâ ïî ññûëêàì è îòïðàâêè ôîðì:
    $link = $crawler->selectLink('Go elsewhere...')->link();
    $crawler = $client->click($link);
    $form = $crawler->selectButton('validate')->form();
    $crawler = $client->submit($form, array('name' => 'Fabien'));

    Ìåòîäû click() è submit() âîçâðàùàþò îáúåêò Crawler. Ýòè ìåòîäû - ëó÷øèé ñïîñîá
    ïðîñìàòðèâàòü âàøå ïðèëîæåíèå, òàê êàê îíè çàáîòÿòñÿ î ìíîãèõ âåùàõ, íàïðèìåð
    îïðåäåëåíèè HTTP ìåòîäà ôîðìû, è ïðåäîñòàâëÿþò âàì óäîáíûé API äëÿ çàãðóçêè
    ôàéëîâ.

    Ñîâåò: Áîëüøå óçíàòü îá îáúåêòàõ Link è Form ìîæíî â ðàçäåëå Crawler .
    Ìåòîä request ìîæåò òàêæå áûòü èñïîëüçîâàí äëÿ ñèìóëÿöèè îòïðàâêè ôîðì èëè äëÿ
    âûïîëíåíèÿ áîëåå ñëîæíûõ çàïðîñîâ:
    // Ïðÿìàÿ îòïðàâêà ôîðìû (ìîæíî è òàê, íî ëåã÷å èñïîëüçîâàòü Crawler!)
    $client->request('POST', '/submit', array('name' => 'Fabien'));

    154

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // Îòïðàâêà ôîðìû ñ çàãðóçêîé ôàéëà
    use Symfony\Component\HttpFoundation\File\UploadedFile;
    $photo = new UploadedFile(
    '/path/to/photo.jpg',
    'photo.jpg',
    'image/jpeg',
    123
    );
    // èëè
    $photo = array(
    'tmp_name' => '/path/to/photo.jpg',
    'name' => 'photo.jpg',
    'type' => 'image/jpeg',
    'size' => 123,
    'error' => UPLOAD_ERR_OK
    );
    $client->request(
    'POST',
    '/submit',
    array('name' => 'Fabien'),
    array('photo' => $photo)
    );
    // Âûïîëíåíèå DELETE çàïðîñîâ, è îòïðàâêà HTTP çàãîëîâêîâ
    $client->request(
    'DELETE',
    '/post/12',
    array(),
    array(),
    array('PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'pa$$word')
    );

    È ïîñëåäíåå, íî íå ìåíåå âàæíîå, ìîæíî çàñòàâèòü êàæäûé çàïðîñ âûïîëíÿòüñÿ â ñîáñòâåííîì ïðîöåññå PHP ÷òîáû èçáåæàòü ëþáûõ ïîáî÷íûõ ýôôåêòîâ êîãäà íåñêîëüêî
    êëèåíòîâ ðàáîòàþò â îäíîì ñêðèïòå:
    $client->insulate();

    Áðàóçèíã

    Êëèåíò ïîääåðæèâàåò ìíîãèå îïåðàöèè, ñâîéñòâåííûå íàñòîÿùåìó áðàóçåðó:
    $client->back();
    $client->forward();

    2.9.

    Òåñòèðîâàíèå

    155

    Symfony Documentation, Âûïóñê 2.0

    $client->reload();
    // Î÷èùàåò âñå êóêè è èñòîðèþ.
    $client->restart();

    Ïîëó÷åíèå âíóòðåííèõ îáúåêòîâ

    Êîãäà êëèåíò èñïîëüçóåòñÿ äëÿ òåñòèðîâàíèÿ ïðèëîæåíèÿ, âîçíèêàåò íåîáõîäèìîñòü
    ïîëó÷èòü äîñòóï ê åãî âíóòðåííèì îáúåêòàì:
    $history = $client->getHistory();
    $cookieJar = $client->getCookieJar();

    Òàêæå ìîæíî ïîëó÷èòü îáúåêòû, îòíîñÿùèåñÿ ê ïîñëåäíåìó çàïðîñó:
    $request = $client->getRequest();
    $response = $client->getResponse();
    $crawler = $client->getCrawler();

    Åñëè çàïðîñû íå áûëè èçîëèðîâàíû, òî ìîæíî ïîëó÷èòü äîñòóï ê Container è Kernel:
    $container = $client->getContainer();
    $kernel
    = $client->getKernel();

    Ïîëó÷åíèå Container

    Íàñòîÿòåëüíî ðåêîìåíäóåòñÿ èñïîëüçîâàòü ôóíêöèîíàëüíûå òåñòû òîëüêî äëÿ ïðîâåðêè Response. Íî â íåêîòîðûõ ðåäêèõ ñëó÷àÿõ íåîáõîäèìî ïîëó÷èòü äîñòóï ê êàêèì-ëèáî
    âíóòðåííèì îáúåêòàì äëÿ íàïèñàíèÿ óòâåðæäåíèé. Äëÿ ýòîãî ìîæíî èñïîëüçîâàòü êîíòåéíåð âíåäðåíèÿ çàâèñèìîñòè:
    $container = $client->getContainer();

    Èìåéòå â âèäó ÷òî ýòî íå ñðàáîòàåò åñëè âû èçîëèðîâàëè êëèåíòà èëè èñïîëüçîâàëè
    HTTP ñëîé. Äëÿ ïîëó÷åíèÿ ñïèñêà ñëóæá, äîñòóïíûõ â âàøåì ïðèëîæåíèè, èñïîëüçóéòå êîíñîëüíóþ êîìàíäó container:debug.

    Ñîâåò: Åñëè íåîáõîäèìàÿ äëÿ ïðîâåðêè èíôîðìàöèÿ äîñòóïíà èç ïðîôèëèðîâùèêà,
    òîãäà èñïîëüçóéòå åãî.

    156

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïîëó÷åíèå äàííûõ ïðîôèëèðîâùèêà

    Äëÿ êàæäîãî çàïðîñà ïðîôàéëåð Symfony ñîáèðàåò è ñîõðàíÿåò ìíîæåñòâî äàííûõ î
    òîì êàê îáðàáàòûâàåòñÿ ýòîò çàïðîñ. Íàïðèìåð, ïðîôàéëåð ìîæåò áûòü èñïîëüçîâàí
    äëÿ âåðèôèêàöèè, ÷òî äàííàÿ ñòðàíèöà âûïîëíÿåò SQL çàïðîñîâ ìåíüøå, ÷åì íåêîòîðîå
    ïîðîãîâîå çíà÷åíèå.
    Äëÿ ïîëó÷åíèÿ ïðîôàéëåðà äëÿ ïîñëåäíåãî çàïðîñà âûïîëíèòå ñëåäóþùèé êîä:
    $profile = $client->getProfile();

    Ïîäðîáíåå ïðî èñïîëüçîâàíèå ïðîôàéëåðà â òåñòàõ ÷èòàéòå â êíèãå ðåöåïòîâ:
    ïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå .

    Êàê èñ-

    Ïåðåíàïðàâëåíèå

    Êîãäà çàïðîñ âîçâðàùàåò îòâåò ñ ïåðåíàïðàâëåíèåì, êëèåíò àâòîìàòè÷åñêè ñëåäóåò åìó.
    Åñëè âû õîòèòå ïðîâåðèòü îòâåò ïåðåä ïåðåíàïðàâëåíèåì, âû ìîæåòå óêàçàòü êëèåíòó
    íå ñëåäîâàòü ïåðåíàïðàâëåíèþ ïðèïîìîùè ìåòîäà followRedirects():
    $client->followRedirects(false);

    Åñëè êëèåíò íå ñëåäóåò ïåðåíàïðàâëåíèÿì, âû ìîæåòå ôîðñèðîâàòü ïåðåíàïðàâëåíèå
    ïðè ïîìîùè ìåòîäà followRedirect():
    $crawler = $client->followRedirect();

    2.9.5 Crawler

    Ýêçåìïëÿð Crawler âîçâðàùàåòñÿ êàæäûé ðàç êîãäà âûïîëíÿåòñÿ çàïðîñ ïîñðåäñòâîì
    êëèåíòà. Îí ïîçâîëÿåò ïåðåìåùàòüñÿ ïî HTML äîêóìåíòàì, âûáèðàòü óçëû, èñêàòü
    ññûëêè è ôîðìû.
    Ïåðåìåùåíèÿ

    Êàê è jQuery, Crawler èìååò ìåòîäû äëÿ ïåðåìåùåíèÿ ïî DOM äîêóìåíòà HTML/XML.
    Íàïðèìåð, ñëåäóþùèé êîä íàõîäèò âñå ýëåìåíòû input[type=submit], âûáèðàåò ïîñëåäíèé íà ñòðàíèöå è âûáèðàåò áëèæàéøèé ðîäèòåëüñêèé ýëåìåíò:
    $newCrawler = $crawler->filter('input[type=submit]')
    ->last()
    ->parents()
    ->first()
    ;
    2.9.

    Òåñòèðîâàíèå

    157

    Symfony Documentation, Âûïóñê 2.0

    Òàêæå äîñòóïíû ñëåäóþùèå ìåòîäû:

    Ìåòîä

    filter('h1.title')
    filterXpath('h1')
    eq(1)
    first()
    last()
    siblings()
    nextAll()
    previousAll()
    parents()
    children()
    reduce($lambda)

    Îïèñàíèå

    Íîäû, ñîîòâåòñòâóþùèå CSS ñåëåêòîðó
    Íîäû, ñîîòâåòñòâóþùèå âûðàæåíèþ XPath
    Íîäû ñ îïðåäåë¼ííûì èíäåêñîì
    Ïåðâûé íîä
    Ïîñëåäíèé íîä
    Ýëåìåíòû îäíîãî óðîâíÿ (ñ¼ñòðû)
    Âñå ïîñëåäóþùèå ñ¼ñòðû
    Âñå ïðåäûäóùèå ñ¼ñòðû
    Ðîäèòåëüñêèå íîäû
    Ïîòîìêè
    Íîäû, äëÿ êîòîðûõ ôóíêöèÿ íå âîçâðàùàåò false

    Òàê êàê êàæäûé ìåòîä âîçâðàùàåò íîâûé ýêçåìïëÿð Crawler, âû ìîæåòå óïðîñòèòü
    âàø êîä ïóò¼ì âûñòðàèâàíèÿ âûçîâîâ â öåïî÷êó:
    $crawler
    ->filter('h1')
    ->reduce(function ($node, $i)
    {
    if (!$node->getAttribute('class')) {
    return false;
    }
    })
    ->first();

    Ñîâåò: Èñïîëüçóéòå ôóíêöèþ count() ÷òîáû ïîëó÷èòü êîëè÷åñòâî óçëîâ, õðàíÿùèõñÿ
    â Crawler: count($crawler)

    Èçâëå÷åíèå èíôîðìàöèè

    Crawler ìîæåò èçâëå÷ü èíôîðìàöèþ èç óçëîâ:
    // Âîçâðàùàåò çíà÷åíèå àòðèáóòà äëÿ ïåðâîãî óçëà
    $crawler->attr('class');
    // Âîçâðàùàåò çíà÷åíèå óçëà äëÿ ïåðâîãî óçëà
    $crawler->text();
    // Âîçâðàùàåò ìàññèâ äëÿ êàæäîãî ýëåìåíòà ñ åãî çíà÷åíèåì è ññûëêîé
    $info = $crawler->extract(array('_text', 'href'));

    158

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // Âûïîëíÿåò lambda äëÿ êàæäîãî óçëà è âîçâðàùàåò ìàññèâ ðåçóëüòàòîâ
    $data = $crawler->each(function ($node, $i)
    {
    return $node->attr('href');
    });

    Ññûëêè

    Ìîæíî âûáèðàòü ññûëêè ñ ïîìîùüþ ìåòîäîâ îáõîäà, íî ñîêðàùåíèå selectLink() ÷àñòî
    áîëåå óäîáíî:
    $crawler->selectLink('Click here');

    Îíî âûáèðàåò ññûëêè, ñîäåðæàùèå óêàçàííûé òåêñò, ëèáî èçîáðàæåíèÿ, ïî êîòîðûì
    ìîæíî êëèêàòü, ñîäåðæàùèå ýòîò òåêñò â àòðèáóòå alt.
    Êëèåíòñêèé ìåòîä click() ïðèíèìàåò ýêçåìïëÿð Link, âîçâðàùàåìûé ìåòîäîì link():
    $link = $crawler->link();
    $client->click($link);

    Ñîâåò: Ìåòîä links() âîçâðàùàåò ìàññèâ îáúåêòîâ Link äëÿ âñåõ óçëîâ.

    Ôîðìû

    Êàê è ññûëêè, ôîðìû âûáèðàéòå ìåòîäîì selectButton():
    $crawler->selectButton('submit');

    Çàìåòüòå ÷òî âûáèðàåòñÿ êíîïêà íà ôîðìå, à íå ñàìà ôîðìà, ò. ê. îíà ìîæåò èìåòü
    íåñêîëüêî êíîïîê; åñëè èñïîëüçóþòñÿ API ïåðåìåùåíèé, òî ïîìíèòå ÷òî íàäî èñêàòü
    êíîïêó.
    Ìåòîä selectButton() ìîæåò âûáðàòü òåãè button è input ñ òèïîì submit; â í¼ì çàëîæåíî íåñêîëüêî ýâðèñòèê äëÿ èõ íàõîæäåíèÿ ïî:
    ˆ çíà÷åíèþ àòðèáóòà value;
    ˆ çíà÷åíèþ àòðèáóòà id èëè alt äëÿ èçîáðàæåíèé;
    ˆ çíà÷åíèþ àòðèáóòà id èëè name äëÿ òåãîâ button.
    Êîãäà èìååòñÿ óçåë, îïèñûâàþùèé êíîïêó, âûçîâèòå ìåòîä form() ÷òîáû ïîëó÷èòü ýêçåìïëÿð Form, ôîðìû îá¼ðòûâàþùåé åãî:

    2.9.

    Òåñòèðîâàíèå

    159

    Symfony Documentation, Âûïóñê 2.0

    $form = $buttonCrawlerNode->form();

    Ïðè âûçîâå ìåòîäà form() ìîæíî ïåðåäàòü ìàññèâ çíà÷åíèé äëÿ ïîëåé, ïåðåçàïèñûâàþùèõ íà÷àëüíûå çíà÷åíèÿ:
    $form = $buttonCrawlerNode->form(array(
    'name'
    => 'Fabien',
    'my_form[subject]' => 'Symfony rocks!',
    ));

    À åñëè íàäî ñèìóëèðîâàòü îïðåäåë¼ííûé HTTP ìåòîä äëÿ ôîðìû, ïåðåäàéòå åãî âòîðûì àðãóìåíòîì:
    $form = $crawler->form(array(), 'DELETE');

    Êëèåíò ìîæåò îòïðàâëÿòü ýçêåìïëÿðû Form:
    $client->submit($form);

    Çíà÷åíèÿ ïîëåé ìîãóò áûòü ïåðåäàíû âòîðûì àðãóìåíòîì ìåòîäà submit():
    $client->submit($form, array(
    'name'
    => 'Fabien',
    'my_form[subject]' => 'Symfony rocks!',
    ));

     áîëåå ñëîæíûõ ñëó÷àÿõ, èñïîëüçóéòå ýêçåìïëÿð Form êàê ìàññèâ ÷òîáû çàäàòü çíà÷åíèÿ êàæäîãî ïîëÿ èíäèâèäóàëüíî:
    // Èçìåíÿåò çíà÷åíèå ïîëÿ
    $form['name'] = 'Fabien';
    $form['my_form[subject]'] = 'Symfony rocks!';

    Çäåñü òîæå åñòü êðàñèâûé API äëÿ óïðàâëåíèÿ çíà÷åíèÿìè ïîëåé â çàâèñèìîñòè îò èõ
    òèïîâ:
    // Âûáèðàåò option èëè radio
    $form['country']->select('France');
    // Ñòàâèò ãàëî÷êó â checkbox
    $form['like_symfony']->tick();
    // Çàãðóæàåò ôàéë
    $form['photo']->upload('/path/to/lucas.jpg');

    Ñîâåò:

    Ìîæíî ïîëó÷èòü çíà÷åíèÿ, êîòîðûå áóäóò îòïðàâëåíû, âûçâàâ ìåòîä
    getValues() îáúåêòà Form. Çàãðóæàåìûå ôàéëû äîñòóïíû â îòäåëüíîì ìàññèâå, âîçâðàùàåìîì ÷åðåç getFiles(). getPhpValues() è getPhpFiles() òîæå âîçâðàùàþò çíà÷å160

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    íèÿ äëÿ îòïðàâêè, íî â ôîðìàòå PHP (îí ïðåîáðàçóåò êëþ÷è ñ êâàäðàòíûìè ñêîáêàìè
    - íàïðèìåð, my_form[subject] - â PHP ìàññèâû).

    2.9.6 Òåñòîâàÿ êîíôèãóðàöèÿ
    PHPUnit êîíôèãóðàöèÿ

    Êàæäîå ïðèëîæåíèå èìååò ñâîþ ñîáñòâåííóþ êîíôèãóðàöèþ PHPUnit, êîòîðàÿ õðàíèòñÿ â ôàéëå phpunit.xml.dist. Âû ìîæåòå ðåäàêòèðîâàòü ýòîò ôàéë è ìåíÿòü çíà÷åíèÿ
    ïî óìîë÷àíèþ èëè æå âû ìîæåòå ñîçäàòü ôàéë phpunit.xml äëÿ ïîäãîíêè êîíôèãóðàöèè íà âàøåé ëîêàëüíîé ìàøèíå.

    Ñîâåò: Ñîõðàíèåòå ôàéë phpunit.xml.dist â âàøåì ðåïîçèòîðèè è èãíîðüòå ôàéë
    phpunit.xml.

    Ïî óìîë÷àíèþ, ïî êîìàíäå phpunit çàïóñêàþòñÿ òîëüêî òåñòû èç ñòàíäàðòíûõ
    ïàêåòîâ (ñòàíäàðòíûìè ñ÷èòàþòñÿ òåñòû â äèðåêòîðèÿõ src/*/Bundle/Tests èëè
    src/*/Bundle/*Bundle/Tests), íî âû ìîæåòå çàïðîñòî äîáàâèòü áîëüøå äèðåêòîðèé.
    Íàïðèìåð, ñëåäóþùàÿ êîíôèãóðàöèÿ äîáàâëÿåò òåñòû ñòîðîííèõ ïàêåòîâ:



    ../src/*/*Bundle/Tests
    ../src/Acme/Bundle/*Bundle/Tests



    Äëÿ òîãî, ÷òîáû âêëþ÷èòü ïðî÷èå äèðåêòîðèè â îò÷¼ò ïî ïîêðûòèþ êîäà, íåîáõîäèìî
    îòðåäàêòèðîâàòü ñåêöèþ :


    ../src

    ../src/*/*Bundle/Resources
    ../src/*/*Bundle/Tests
    ../src/Acme/Bundle/*Bundle/Resources
    ../src/Acme/Bundle/*Bundle/Tests




    2.9.

    Òåñòèðîâàíèå

    161

    Symfony Documentation, Âûïóñê 2.0

    2.9.7 Óçíàéòå áîëüøå èç Ðåöåïòîâ

    ˆ

    Êàê ñìîäåëèðîâàòü HTTP àóòåíòèôèêàöèþ â Ôóíêöèîíàëüíîì òåñòå

    ˆ

    Êàê òåñòèðîâàòü âçàèìîäåéñòâèå ñ íåñêîëüêèìè êëèåíòàìè

    ˆ

    Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå

    2.10 Âàëèäàöèÿ
    Âàëèäàöèÿ - ýòî âïîëíå îáû÷íàÿ çàäà÷à äëÿ web-ïðèëîæåíèÿ. Äàííûå, ââîäèìûå â
    ôîðìû äîëæíû áûòü âàëèäèðîâàíû (ïðîâåðåíû). Â òî æå âðåìÿ, äàííûå äîëæíû áûòü
    âàëèäèðîâàíû äî òîãî, êàê îíè áóäóò çàïèñàíû â áàçó äàííûõ èëè æå áóäóò ïåðåäàíû
    äàëåå íåêîòîðîìó web-ñåðâèñó.
    Symfony2 ñîäåðæèò êîìïîíåíò Validator äëÿ òîãî, ÷òîáû óïðîñòèòü ýòó çàäà÷ó. Ýòîò
    êîìïîíåíò îñíîâàí íà äîêóìåíòå JSR303 Bean Validation specication. ×òî?! Java ñïåöèôèêàöèÿ â PHP? Îäíàêî æå âû âñ¼ óñëûøàëè âåðíî, íî âñ¼ íå òàê ïëîõî êàê âàì
    ìîãëî ïîêàçàòüñÿ. Äàâàéòå ïîñìîòðèì, êàê ìû ìîæåì èñïîëüçîâàòü ýòî â PHP.
    2.10.1 Îñíîâû Âàëèäàöèè

    Ñàìûé ëó÷øèé ñïîñîá ïîíÿòü âàëèäàöèþ - ýòî óâèäåòü å¼ â äåéñòâèè. Äëÿ íà÷àëà,
    ïðåäïîëîæèì, ÷òî âû ñîçäàëè îáû÷íûé PHP-îáúåêò è âàì íóæíî èñïîëüçîâàòü åãî
    ãäå-òî âíóòðè ïðèëîæåíèÿ:
    // src/Acme/BlogBundle/Entity/Author.php
    namespace Acme\BlogBundle\Entity;
    class Author
    {
    public $name;
    }

    Èòàê, ýòî îáû÷íûé êëàññ, êîòîðûé îáñëóæèâàåò íåêîòîðûé êðóã çàäà÷ âíóòðè âàøåãî
    ïðèëîæåíèÿ. Öåëü âàëèäàöèè çàêëþ÷àåòñÿ â òîì, ÷òîáû ñîîáùèòü âàì - ÿâëÿþòñÿ ëè
    äàííûå îáúåêòà êîððåêòíûìè (âàëèäíûìè). Äëÿ ýòîãî, âàì íóæíî íàñòðîèòü ïåðå÷åíü
    ïðàâèë (íàçûâàåìûõ Îãðàíè÷åíèÿìè ), êîòîðûì îáúåêò äîëæåí óäîâëåòâîðÿòü, ÷òîáû
    áûòü âàëèäíûì. Ýòè ïðàâèëà ìîãóò áûòü óêàçàíû âî ìíîãèõ ôîðìàòàõ (YAML, XML,
    àííîòàöèè, èëè PHP).
    Íàïðèìåð, äëÿ òîãî, ÷òîáû ãàðàíòèðîâàòü, ÷òî ñâîéñòâî $name íå ïóñòî, äîáàâüòå ñëåäóþùèé êîä:

    162

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author:
    properties:
    name:
    - NotBlank: ~

    ˆ

    Annotations

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    /**
    * @Assert\NotBlank()
    */
    public $name;
    }

    ˆ

    XML



    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com







    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\NotBlank;
    class Author
    {
    public $name;
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    2.10.

    Âàëèäàöèÿ

    163

    Symfony Documentation, Âûïóñê 2.0

    {
    }

    }

    $metadata->addPropertyConstraint('name', new NotBlank());

    Ñîâåò: Çàùèù¼ííûå (protected) è çàêðûòûå (private) ÷ëåíû êëàññà ìîãóò áûòü òàêæå
    âàëèäèðîâàíû êàê è ëþáîé get-ìåòîä (ñì. validator-constraint-targets).

    Èñïîëüçîâàíèå ñåðâèñà

    validator

    Äàëåå, ÷òîáû ïðîâåðèòü îáúåêò Author, èñïîëüçóéòå ìåòîä validate ñåðâèñà validator
    (class Symfony\Component\Validator\Validator). Îáÿçàííîñòè ó êëàññà validator ïðîñòûå: ïðî÷èòàòü îãðàíè÷åíèÿ (ò.å. ïðàâèëà), îïðåäåë¼ííûå äëÿ êëàññà, è îïðåäåëèòü,
    óäîâëåòâîðÿþò ëè äàííûå èç îáúåêòà ýòèì îãðàíè÷åíèÿì. Åñëè âàëèäàöèÿ ïðîõîäèò ñ
    îøèáêîé, âîçâðàùàåò ìàññèâ îøèáîê. Äàâàéòå ðàññìîòðèì ýòîò ïðîñòîé ïðèìåð êîíòðîëëåðà:
    use Symfony\Component\HttpFoundation\Response;
    use Acme\BlogBundle\Entity\Author;
    // ...
    public function indexAction()
    {
    $author = new Author();
    // ... âûïîëíÿþòñÿ êàêèå-ëèáî äåéñòâèÿ ñ îáúåêòîì $author
    $validator = $this->get('validator');
    $errors = $validator->validate($author);

    }

    if (count($errors) > 0) {
    return new Response(print_r($errors, true));
    } else {
    return new Response('The author is valid! Yes!');
    }

    Åñëè ñâîéñòâî $name ïóñòîå, âû óâèäèòå ñëåäóþùóþ îøèáêó:
    Acme\BlogBundle\Author.name:
    This value should not be blank

    Åñëè æå âû óêàæåòå íåêîòîðîå íåïóñòîå çíà÷åíèå äëÿ name, ïîÿâèòñÿ ñîîáùåíèå îá
    óñïåøíîé âàëèäàöèè.

    164

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Áîëüøóþ ÷àñòü âðåìåíè âû íå áóäåòå íàïðÿìóþ âçàèìîäåéñòâîâàòü ñ ñåðâè-

    ñîì validator è âàì íå íóæíî áóäåò áåñïîêîèòüñÿ îá îòîáðàæåíèè îøèáîê. Áîëüøóþ
    ÷àñòü âðåìåíè âû áóäåòå èñïîëüçîâàòü âàëèäàöèþ êîñâåííî ïðè îáðàáîòêå äàííûõ èç
    îòïðàâëåííûõ ïðèëîæåíèþ ôîðì. Ïîäðîáíåå îá ýòîì íàïèñàíî â ñåêöèè: Âàëèäàöèÿ è
    Ôîðìû .
    Âû òàêæå ìîæåòå ïåðåäàòü ïåðå÷åíü îøèáîê â øàáëîí.
    if (count($errors) > 0) {
    return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
    'errors' => $errors,
    ));
    } else {
    // ...
    }

    Âíóòðè øàáëîíà âû ìîæåòå îòîáðàçèòü ñïèñîê îøèáîê òàê, êàê âàì íóæíî:
    ˆ

    Twig

    {# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}

    The author has the following errors



      {% for error in errors %}
    • {{ error.message }}

    • {% endfor %}


    ˆ

    PHP


    The author has the following errors




    • getMessage() ?>




    Ïðèìå÷àíèå: Êàæäàÿ îøèáêà âàëèäàöèè (íàçûâàåìàÿ constraint violation), ïðåäñòàâëåíà îáúåêòîì êëàññà Symfony\Component\Validator\ConstraintViolation

    2.10.

    Âàëèäàöèÿ

    165

    Symfony Documentation, Âûïóñê 2.0

    Âàëèäàöèÿ è Ôîðìû

    Ñåðâèñ validator ìîæåò áûòü èñïîëüçîâàí â ëþáîå âðåìÿ äëÿ ïðîâåðêè îáúåêòà. Â
    æèçíè æå, íå ñìîòðÿ òàêóþ âîçìîæíîñòü, âû áóäåòå ðàáîòàòü ñ ñåðâèñîì validator
    êîñâåííî ïðè îáðàáîòêå ôîðì. Áèáëèîòåêà ôîðì Symfony èñïîëüçóåò ñåðâèñ âàëèäàöèè
    äëÿ ïðîâåðêè îáúåêòîâ ôîðì ïîñëå òîãî, êàê äàííûå áûëè îòïðàâëåíû ïîëüçîâàòåëåì
    è ïðèâÿçàíû ê ôîðìå. Îáúåêòû îøèáîê âàëèäàöèè (constraint violations) áóäóò êîíâåðòèðîâàíû â îáúåêòû FieldError, êîòîðûå ìîãóò áûòü ëåãêî îòîáðàæåíû âìåñòå ñ
    ôîðìàìè. Òèïè÷íûé ïðîöåññ îòïðàâêè ôîðìû ñî ñòîðîíû êîíòðîëëåðà âûãëÿäèò òàê:
    use Acme\BlogBundle\Entity\Author;
    use Acme\BlogBundle\Form\AuthorType;
    use Symfony\Component\HttpFoundation\Request;
    // ...
    public function updateAction(Request $request)
    {
    $author = new Acme\BlogBundle\Entity\Author();
    $form = $this->createForm(new AuthorType(), $author);
    if ($request->getMethod() == 'POST') {
    $form->bindRequest($request);
    if ($form->isValid()) {
    // âàëèäàöèÿ ïðîøëà óñïåøíî, ìîæíî âûïîëíÿòü äàëüíåéøèå äåéñòâèÿ ñ îáúåêòîì $author

    }

    }

    }

    $this->redirect($this->generateUrl('...'));

    return $this->render('BlogBundle:Author:form.html.twig', array(
    'form' => $form->createView(),
    ));

    Ïðèìå÷àíèå: Ýòîò ïðèìåð èñïîëüçóåò êëàññ ôîðìû AuthorType, êîòîðûé â ýòîé ãëàâå
    íå îïèñàí.

    Áîëåå ïîäðîáíóþ èíôîðìàöèþ î ôîðìàõ âû ìîæåòå ïîëó÷èòü â ãëàâå

    Ôîðìû .

    2.10.2 Êîíôèãóðèðîâàíèå

    Âàëèäàòîð Symfony2 äîñòóïåí ïî óìîë÷àíèþ, íî âû äîëæíû òùàòåëüíî íàñòðîèòü åãî
    ïðè ïîìîùè àííîòàöèé (åñëè âû èñïîëüçóåòå ìåòîä àííîòàöèé äëÿ íàñòðîéêè îãðàíè166

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ÷åíèé):
    ˆ

    YAML

    # app/config/config.yml
    framework:
    validation: { enable_annotations: true }

    ˆ

    XML






    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array('validation' => array(
    'enable_annotations' => true,
    )));

    2.10.3 Îãðàíè÷åíèÿ

    Âàëèäàòîð ñîçäàí äëÿ òîãî, ÷òîáû ïðîâåðÿòü îáúåêòû íà ñîîòâåòñòâèå îãðàíè÷åíèÿì
    (ò.å. ïðàâèëàì). Äëÿ òîãî ÷òîáû âàëèäèðîâàòü îáúåêò, óêàæèòå äëÿ åãî êëàññà îäíî èëè
    áîëåå îãðàíè÷åíèé è ïåðåäàéòå åãî ñåðâèñó âàëèäàöèè (validator).
    Ïî ñóòè, îãðàíè÷åíèå - ýòî PHP îáúåêò, êîòîðûé âûïîëíÿåò ïðîâåðî÷íîå âûðàæåíèå.
     æèçíè îãðàíè÷åíèå ìîæåò âûãëÿäåòü òàê: ïèðîã íå äîëæåí ïîäãîðåòü.  Symfony2
    îãðàíè÷åíèÿ âûãëÿäÿò ïîõîæèì îáðàçîì: ýòî óòâåðæäåíèÿ, ÷òî íåêîòîðîå âûðàæåíèå
    èñòèííî. Ó÷èòûâàÿ çíà÷åíèå, îãðàíè÷åíèå ñêàæåò âàì, ñîîòâåòñòâóåò ëè ýòî çíà÷åíèå
    ïðàâèëó îãðàíè÷åíèÿ.
    Ïîääåðæèâàåìûå îãðàíè÷åíèÿ

    Symfony2 ñîäåðæèò áîëüøîå êîëè÷åñòâî îãðàíè÷åíèé, íåîáõîäèìûõ â ïîâñåäíåâíîé ðàáîòå:
    Áàçîâûå îãðàíè÷åíèÿ

    Íèæå ïåðå÷èñëåíû áàçîâûå îãðàíè÷åíèÿ: îíè èñïîëüçóþòñÿ äëÿ ñàìûõ ïðîñòûõ ïðîâåðîê ïîëåé êëàññîâ èëè çíà÷åíèé, âîçâðàùàåìûõ ìåòîäàìè îáúåêòîâ.
    ˆ NotBlank
    2.10.

    Âàëèäàöèÿ

    167

    Symfony Documentation, Âûïóñê 2.0

    ˆ Blank
    ˆ NotNull
    ˆ Null
    ˆ True
    ˆ False
    ˆ Type
    Ñòðîêîâûå îãðàíè÷åíèÿ

    ˆ Email
    ˆ MinLength
    ˆ MaxLength
    ˆ Url
    ˆ Regex
    ˆ Ip
    ×èñëîâûå îãðàíè÷åíèÿ

    ˆ Max
    ˆ Min
    Îãðàíè÷åíèÿ äàò

    ˆ Date
    ˆ DateTime
    ˆ Time
    Îãðàíè÷åíèÿ êîëëåêöèé

    ˆ Choice
    ˆ Collection
    ˆ UniqueEntity
    ˆ Language

    168

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ Locale
    ˆ Country
    Îãðàíè÷åíèÿ ôàéëîâ

    ˆ File
    ˆ Image
    Ïðî÷èå îãðàíè÷åíèÿ

    ˆ Callback
    ˆ All
    ˆ UserPassword
    ˆ Valid
    Âû òàêæå ìîæåòå ñîçäàâàòü ñâîè îãðàíè÷åíèÿ. Ýòîò âîïðîñ îñâåùàåòñÿ â òîïèêå
     /cookbook/validation/custom_constraint â êíèãå ðåöåïòîâ.
    Êîíôèãóðàöèÿ îãðàíè÷åíèé

    Íåêîòîðûå îãðàíè÷åíèÿ, êàê íàïðèìåð NotBlank, ïðîñòû, â òî âðåìÿ êàê äðóãèå, êàê íàïðèìåð Choice, èìåþò ìíîãî ðàçëè÷íûõ îïöèé. Ïðåäïîëîæèì, ÷òî êëàññ Author èìååò
    ïîëå gender, êîòîðîå ìîæåò èìåòü äâà çíà÷åíèÿ - male èëè female:
    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author:
    properties:
    gender:
    - Choice: { choices: [male, female], message: Choose a valid gender. }

    ˆ

    Annotations

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    /**
    * @Assert\Choice(
    *
    choices = { "male", "female" },
    *
    message = "Choose a valid gender."
    2.10.

    Âàëèäàöèÿ

    169

    Symfony Documentation, Âûïóñê 2.0

    }

    ˆ

    * )
    */
    public $gender;

    XML



    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com










    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\NotBlank;
    class Author
    {
    public $gender;

    }

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('gender', new Choice(array(
    'choices' => array('male', 'female'),
    'message' => 'Choose a valid gender.',
    )));
    }

    Îïöèè îãðàíè÷åíèÿ âñåãäà ïðåäñòàâëåíû â âèäå ìàññèâà. Îäíàêî, íåêîòîðûå îãðàíè÷åíèÿ òàêæå ïîçâîëÿþò âàì óêàçàòü îäíó îïöèþ - îñíîâíóþ, à íå ìàññèâ îïöèé.  ñëó÷àå
    170

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ñ îãðàíè÷åíèåì Choice, ìîæíî óêàçàòü òîëüêî âàðèàíòû âûáîðà (choices).
    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author:
    properties:
    gender:
    - Choice: [male, female]

    ˆ

    Annotations

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    /**
    * @Assert\Choice({"male", "female"})
    */
    protected $gender;
    }

    ˆ

    XML



    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com



    male
    female





    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\Choice;
    class Author

    2.10.

    Âàëèäàöèÿ

    171

    Symfony Documentation, Âûïóñê 2.0

    {

    }

    protected $gender;
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('gender', new Choice(array('male', 'female')));
    }

    Òàêàÿ âîçìîæíîñòü ïîçâîëÿåò ñäåëàòü íàñòðîéêó áàçîâûõ îïöèé îãðàíè÷åíèÿ êîðî÷å è
    áûñòðåå.
    Åñëè âû íå óâåðåíû, êàê íóæíî óêàçûâàòü îïöèþ, èëè æå ñâåðüòåñü ñ äîêóìåíòàöèåé
    API äëÿ îãðàíè÷åíèÿ èëè æå ïîñòóïàéòå ïðîñòî - âñåãäà ïåðåäàâàéòå ìàññèâ îïöèé (êàê
    ïîêàçàíî âûøå â ïåðâîì ïðèìåðå).
    2.10.4 Öåëè äëÿ îãðàíè÷åíèé

    Îãðàíè÷åíèå ìîãóò áûòü ïðèìåíåíû ê ñâîéñòâó êëàññà (íàïðèìåð, name) èëè æå ê ïóáëè÷íîìó àêñåññîðó (èëè ãåòòåðó, íàïðèìåð, getFullName). Ïåðâûé âàðèàíò íàèáîëåå
    ïðîñòîé è ÷àùå âñåãî âñòðå÷àþùèéñÿ, íî âòîðîé âàðèàíò ïîçâîëÿåò âàì ñîçäàâàòü áîëåå ñëîæíûå ïðàâèëà âàëèäàöèè.
    Ïîëÿ êëàññà

    Âàëèäàöèÿ ïîëåé êëàññà - ýòî íàèáîëåå ïðîñòàÿ òåõíèêà âàëèäàöèè. Symfony2 ïîçâîëÿåò
    âàì âûïîëíÿòü âàëèäàöèþ ïðèâàòíûõ, çàùèù¼ííûõ è ïóáëè÷íûõ ïîëåé. Ñëåäóþùèé
    ëèñòèíã ïîêàçûâàåò êàê íàñòðîèòü ïîëå $firstName êëàññà Author, ÷òîáû îíî èìåëî
    êàê ìèíèìóì òðè ñèìâîëà.
    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author:
    properties:
    firstName:
    - NotBlank: ~
    - MinLength: 3

    ˆ

    Annotations

    // Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    172

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    }

    ˆ

    /**
    * @Assert\NotBlank()
    * @Assert\MinLength(3)
    */
    private $firstName;

    XML





    3



    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\NotBlank;
    use Symfony\Component\Validator\Constraints\MinLength;
    class Author
    {
    private $firstName;

    }

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('firstName', new NotBlank());
    $metadata->addPropertyConstraint('firstName', new MinLength(3));
    }

    Ìåòîäû êëàññà

    Îãðàíè÷åíèÿ òàêæå ìîãóò áûòü ïðèìåíåíû ê çíà÷åíèÿì, âîçâðàùàåìûì ìåòîäàìè.
    Symfony2 ïîçâîëÿåò âàì äîáàâëÿòü îãðàíè÷åíèÿ ê ëþáîìó ïóáëè÷íîìó ìåòîäó, åñëè åãî
    èìÿ íà÷èíàåòñÿ ñ get èëè is.  ýòîì ðóêîâîäñòâå îáà ýòèõ òèïà ìåòîäîâ íàçûâàþòñÿ
    ãåòòåðàìè (îò getters).
    Âûãîäà îò ýòîé òåõíèêè â òîì, ÷òî îíà ïîçâîëÿåò âàì âàëèäèðîâàòü âàø ïðîåêò äèíàìè÷åñêè. Íàïðèìåð, ïðåäïîëîæèì, ÷òî âû õîòèòå áûòü óâåðåííûìè, ÷òî ïîëå ïàðîëÿ íå
    ñîîòâåòñòâóåò èìåíè ïîëüçîâàòåëÿ (ïî ñîîáðàæåíèÿì áåçîïàñíîñòè êîíå÷íî, à íå îò èçëèøíåãî ñíîáèçìà)). Âû ìîæåòå äîñòè÷ü ýòîãî, ñîçäàâ ìåòîä isPasswordLegal è óêàçàâ,
    2.10.

    Âàëèäàöèÿ

    173

    Symfony Documentation, Âûïóñê 2.0

    îãðàíè÷åíèå, ÷òî ýòîò ìåòîä äîëæåí âîçâðàùàòü true:
    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\Author:
    getters:
    passwordLegal:
    - "True": { message: "The password cannot match your first name" }

    ˆ

    Annotations

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    /**
    * @Assert\True(message = "The password cannot match your first name")
    */
    public function isPasswordLegal()
    {
    // return true or false
    }
    }

    ˆ

    XML










    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/Author.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\True;
    class Author
    {
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addGetterConstraint('passwordLegal', new True(array(

    174

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    }

    }

    'message' => 'The password cannot match your first name',
    )));

    Òåïåðü ñîçäàéòå ìåòîä isPasswordLegal() è ðåàëèçóéòå åãî ëîãèêó:
    public function isPasswordLegal()
    {
    return ($this->firstName != $this->password);
    }

    Ïðèìå÷àíèå: Îñîáî âíèìàòåëüíûå ÷èòàòåëè íàâåðíÿêà îòìåòèëè, ÷òî â ïðèìåðå êîí-

    ôèãóðàöèè îïóùåí ïðåôèêñ ãåòòåðà (get èëè is). Ýòî ïîçâîëÿåò âàì ëåãêî ïåðåìåñòèòü îãðàíè÷åíèå íà ïîëå êëàññà ñ òåì æå èìåíåì â ïîñëåäñòâèè (èëè æå, íàîáîðîò, ñ
    ïîëÿ íà ìåòîä êëàññà) áåç èçìåíåíèÿ ëîãèêè âàëèäàöèè.

    Êëàññû

    Íåêîòîðûå îãðàíè÷åíèÿ ïðèìåíÿþòñÿ ê öåëîìó êëàññó âî âðåìÿ âàëèäàöèè. Íàïðèìåð
    îãðàíè÷åíèå Callback - ýòî óíèâåðñàëüíîå îãðàíè÷åíèå, êîòîðîå ïðèìåíÿåòñÿ ê êëàññó
    öåëèêîì. Êîãäà ýòîò êëàññ âàëèäèðóåòñÿ, âûçûâàþòñÿ ìåòîäû óêàçàííûå îãðàíè÷åíèåì,
    ÷òî ïîçâîëÿåò âûïîëíÿòü áîëåå äåòàëüíóþ èëè æå èçáèðàòåëüíóþ âàëèäàöèþ.
    2.10.5 Âàëèäàöèîííûå ãðóïïû

    Äî ñèõ ïîð âû ìîãëè äîáàâëÿòü îãðàíè÷åíèÿ ê êëàññó è óçíàâàòü, óäîâëåòâîðÿåò ëè
    êëàññ âñåì óêàçàííûì äëÿ íåãî îãðàíè÷åíèÿì èëè æå íåò.  íåêîòîðûõ ñëó÷àÿõ, îäíàêî, âàì ìîæåò ïîòðåáîâàòüñÿ âàëèäèðîâàòü îáúåêò, èñïîëüçóÿ ëèøü íåêîòîðûå èç
    îïðåäåë¼ííûõ äëÿ íåãî îãðàíè÷åíèé. Äëÿ òîãî ÷òîáû ïîëó÷èòü òàêóþ âîçìîæíîñòü,
    âû ìîæåòå îïðåäåëèòü êàæäîå îãðàíè÷åíèå â îäíó èëè áîëåå âàëèäàöèîííûõ ãðóïï è
    ïîñëå ýòîãî âûïîëíÿòü âàëèäàöèþ ëèøü äëÿ îäíîé èç ýòèõ ãðóïï.
    Íàïðèìåð, ïîëîæèì ó âàñ åñòü êëàññ User, êîòîðûé èñïîëüçóåòñÿ ïðè ðåãèñòðàöèè ïîëüçîâàòåëÿ è ïðè îáíîâëåíèè åãî ïðîôàéëà âïîñëåäñòâèè:
    ˆ

    YAML

    # src/Acme/BlogBundle/Resources/config/validation.yml
    Acme\BlogBundle\Entity\User:
    properties:
    email:
    - Email: { groups: [registration] }
    password:
    2.10.

    Âàëèäàöèÿ

    175

    Symfony Documentation, Âûïóñê 2.0

    - NotBlank: { groups: [registration] }
    - MinLength: { limit: 7, groups: [registration] }
    city:
    - MinLength: 2

    ˆ

    Annotations

    // src/Acme/BlogBundle/Entity/User.php
    namespace Acme\BlogBundle\Entity;
    use Symfony\Component\Security\Core\User\UserInterface;
    use Symfony\Component\Validator\Constraints as Assert;
    class User implements UserInterface
    {
    /**
    * @Assert\Email(groups={"registration"})
    */
    private $email;
    /**
    * @Assert\NotBlank(groups={"registration"})
    * @Assert\MinLength(limit=7, groups={"registration"})
    */
    private $password;

    }

    ˆ

    /**
    * @Assert\MinLength(2)
    */
    private $city;

    XML











    176

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0








    7



    ˆ

    PHP

    // src/Acme/BlogBundle/Entity/User.php
    namespace Acme\BlogBundle\Entity;
    use
    use
    use
    use

    Symfony\Component\Validator\Mapping\ClassMetadata;
    Symfony\Component\Validator\Constraints\Email;
    Symfony\Component\Validator\Constraints\NotBlank;
    Symfony\Component\Validator\Constraints\MinLength;

    class User
    {
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('email', new Email(array(
    'groups' => array('registration')
    )));
    $metadata->addPropertyConstraint('password', new NotBlank(array(
    'groups' => array('registration')
    )));
    $metadata->addPropertyConstraint('password', new MinLength(array(
    'limit' => 7,
    'groups' => array('registration')
    )));

    }

    }

    $metadata->addPropertyConstraint('city', new MinLength(3));

    Ïðè èñïîëüçîâàíèè òàêîé êîíôèãóðàöèè èìååòñÿ äâå âàëèäàöèîííûå ãðóïïû:
    ˆ Default - ñîäåðæèò îãðàíè÷åíèÿ, íå âêëþ÷¼ííûå íè â îäíó èç ãðóïï;
    ˆ registration - ñîäåðæèò îãðàíè÷åíèÿ äëÿ ïîëåé email è password.
    2.10.

    Âàëèäàöèÿ

    177

    Symfony Documentation, Âûïóñê 2.0

    Äëÿ òîãî, ÷òîáû ÿâíî óêàçàòü âàëèäàòîðó ãðóïïó, ïåðåäàéòå îäíî èëè áîëåå íàèìåíîâàíèé ãðóïï âòîðûì àðãóìåíòîì â ìåòîä validate():
    $errors = $validator->validate($author, array('registration'));

    Êîíå÷íî æå, êàê ïðàâèëî, âû ðàáîòàåòå ñ âàëèäàöèåé êîñâåííî ÷åðåç áèáëèîòåêó Ôîðì.
    ×òîáû óçíàòü êàê èñïîëüçîâàòü ãðóïïû â ôîðìàõ, ñìîòðèòå ðàçäåë Âàëèäàöèîííûå
    ãðóïïû .
    2.10.6 Âàëèäàöèÿ ïðîñòûõ çíà÷åíèé è ìàññèâîâ

    Ðàíåå âû óâèäåëè êàê ìîæíî âàëèäèðîâàòü öåëûå îáúåêòû. Íî èíîãäà, âàì âñåãî ëèøü
    íóæíî âàëèäèðîâàòü ïðîñòîå çíà÷åíèå - íàïðèìåð, ïðîâåðèòü, ÿâëÿåòñÿ ëè ñòðîêà âàëèäíûì email-àäðåñîì. Ýòî òàêæå ëåãêî ñäåëàòü. Âíóòðè êîíòðîëëåðà ýòî áóäåò âûãëÿäåòü òàê:
    // add this to the top of your class
    use Symfony\Component\Validator\Constraints\Email;
    public function addEmailAction($email)
    {
    $emailConstraint = new Email();
    // âñå îïöèè îãðàíè÷åíèÿ ìîæíî çàäàòü òàêèì îáðàçîì
    $emailConstraint->message = 'Invalid email address';
    // èñïîëüçóåì âàëèäàòîð äëÿ ïðîâåðêè çíà÷åíèÿ
    $errorList = $this->get('validator')->validateValue($email, $emailConstraint);
    if (count($errorList) == 0) {
    // ýòî ÂÀËÈÄÍÛÉ àäðåñ, äåëàåì äåëî äàëüøå
    } else {
    // ýòî ÍÅ âàëèäíûé àäðåñ
    $errorMessage = $errorList[0]->getMessage()
    }
    }

    // äåëàåì ÷òî-òî ñ îøèáêîé

    // ...

    Âûçûâàÿ ìåòîä validateValue â âàëèäàòîðå, âû ìîæåòå ïåðåäàòü åìó çíà÷åíèå â âèäå
    ïàðàìåòðà è îáúåêò îãðàíè÷åíèÿ, êîòîðîå õîòèòå ïðîâåðèòü. Ïîëíûé ñïèñîê äîñòóïíûõ
    îãðàíè÷åíèé, à òàêæå ïîëíûå èìåíà êëàññîâ äëÿ êàæäîãî îãðàíè÷åíèÿ, ìîæíî íàéòè â
    Ñïðàâî÷íèêå ïî îãðàíè÷åíèÿì.

    Ìåòîä validateValule âîçâðàùàåò îáúåêò êëàññà Symfony\Component\Validator\ConstraintViolati
    178

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    êîòîðûé, ïî ñóòè, ÿâëÿåòñÿ ìàññèâîì îøèáîê. Êàæäàÿ îøèáêà â êîëëåêöèè - ýòî îáúåêò êëàññà Symfony\Component\Validator\ConstraintViolation, êîòîðûé ñîäåðæèò
    ñîîáùåíèå îá îøèáêå, êîòîðîå ìîæíî ïîëó÷èòü, âûçâàâ ìåòîä getMessage.
    2.10.7 Çàêëþ÷åíèå

    Âàëèäàòîð Symfony2 - ýòî ìîùíûé èíñòðóìåíò, êîòîðûé èñïîëüçóåòñÿ äëÿ ïîëó÷åíèÿ
    ãàðàíòèé, ÷òî äàííûå íåêîòîðîãî îáúåêòà ïðàâèëüíûå. Ñèëà âàëèäàöèè - â îãðàíè÷åíèÿõ, êîòîðûå ÿâëÿþòñÿ ïðàâèëàìè, êîòîðûå ïðèìåíÿþòñÿ ê ïîëÿì êëàññîâ èëè æå èõ
    ãåòòåðàì. È äàæå åñëè âàì ïðèõîäèòñÿ áîëüøåé ÷àñòüþ èñïîëüçîâàòü ôðåéìâîðê äëÿ
    âàëèäàöèè êîñâåííî - ñîâìåñòíî ñ ôîðìàìè, ïîìíèòå, ÷òî îí ìîæåò áûòü èñïîëüçîâàí
    ãäå óãîäíî äëÿ âàëèäàöèè ëþáîãî îáúåêòà.
    2.10.8 Äîïîëíèòåëüíî â êíèãå ðåöåïòîâ:

    ˆ /cookbook/validation/custom_constraint

    2.11 Ôîðìû
    Ðàáîòà ñ ôîðìàìè - îäíà èç íàèáîëåå òèïè÷íûõ è ïðîáëåìíûõ çàäà÷ äëÿ webðàçðàáîò÷èêà. Symfony2 âêëþ÷àåò êîìïîíåíò äëÿ ðàáîòû ñ Ôîðìàìè, êîòîðûé îáëåã÷àåò ðàáîòó ñ íèìè.  ýòîé ãëàâå âû óçíàåòå, êàê ñîçäàâàòü ñëîæíûå ôîðìû ñ íóëÿ,
    ïîçíàêîìèòåñü ñ íàèáîëåå âàæíûìè îñîáåííîñòÿìè áèáëèîòåêè ôîðì.

    Ïðèìå÷àíèå: Êîìïîíåíò äëÿ ðàáîòû ñ ôîðìàìè - ýòî íåçàâèñèìàÿ áèáëèîòåêà, êîòîðàÿ ìîæåò áûòü èñïîëüçîâàíà âíå ïðîåêòîâ Symfony2. Ïîäðîáíîñòè èùèòå ïî ññûëêå
    Symfony2 Form Component íà ÃèòÕàáå.

    2.11.1 Ñîçäàíèå ïðîñòîé ôîðìû

    Ïðåäïîëîæèì, âû ðàáîòàåòå íàä ïðîñòûì ïðèëîæåíèåì - ñïèñêîì ToDo, êîòîðîå áóäåò
    îòîáðàæàòü íåêîòîðûå çàäà÷è. Ïîñêîëüêó âàøèì ïîëüçîâàòåëÿì áóäåò íåîáõîäèìî
    ñîçäàâàòü è ðåäàêòèðîâàòü çàäà÷è, âàì ïîòðåáóåòñÿ ñîçäàòü ôîðìó. Íî, ïðåæäå ÷åì
    íà÷àòü, äàâàéòå ñîçäàäèì áàçîâûé êëàññ Task, êîòîðûé ïðåäñòàâëÿåò è õðàíèò äàííûå
    äëÿ îäíîé çàäà÷è:
    // src/Acme/TaskBundle/Entity/Task.php
    namespace Acme\TaskBundle\Entity;

    2.11.

    Ôîðìû

    179

    Symfony Documentation, Âûïóñê 2.0

    class Task
    {
    protected $task;
    protected $dueDate;
    public function getTask()
    {
    return $this->task;
    }
    public function setTask($task)
    {
    $this->task = $task;
    }

    }

    public function getDueDate()
    {
    return $this->dueDate;
    }
    public function setDueDate(\DateTime $dueDate = null)
    {
    $this->dueDate = $dueDate;
    }

    Ïðèìå÷àíèå: Åñëè âû áóäåòå áðàòü êîä ïðèìåðîâ îäèí â îäèí, âàì, ïðåæäå âñåãî
    íåîáõîäèìî ñîçäàòü ïàêåò AcmeTaskBundle, âûïîëíèâ ñëåäóþùóþ êîìàíäó (è ïðèíèìàÿ
    âñå îïöèè èíòåðàêòèâíîãî ãåíåðàòîðà ïî óìîë÷àíèþ):
    php app/console generate:bundle --namespace=Acme/TaskBundle

    Ýòîò êëàññ ïðåäñòàâëÿåò ñîáîé îáû÷íûé PHP-îáúåêò è íå èìååò íè÷åãî îáùåãî ñ
    Symfony èëè êàêîé-ëèáî äðóãîé áèáëèîòåêîé. Ýòî PHP-îáúåêò, êîòîðûé âûïîëíÿåò çàäà÷ó íåïîñðåäñòâåííî âíóòðè âàøåãî ïðèëîæåíèå (ò.å. ÿâëÿåòñÿ ïðåäñòàâëåíèåì çàäà÷è
    â âàøåì ïðèëîæåíèè). Êîíå÷íî æå, ê êîíöó ýòîé ãëàâû âû áóäåòå èìåòü âîçìîæíîñòü
    îòïðàâëÿòü äàííûå äëÿ ýêçåìïëÿðà Task (ïîñðåäñòâîì HTML-ôîðìû), âàëèäèðîâàòü
    å¼ äàííûå è ñîõðàíÿòü â áàçó äàííûõ.
    Ñîçäàíèå ôîðìû

    Òåïåðü, êîãäà âû ñîçäàëè êëàññ Task, ñëåäóþùèì øàãîì áóäåò ñîçäàíèå è îòîáðàæåíèå
    HTML-ôîðìû. Â Symfony2 ýòî âûïîëíÿåòñÿ ïîñðåäñòâîì ñîçäàíèÿ îáúåêòà ôîðìû è
    îòîáðàæåíèÿ åãî â øàáëîíå. Òåïåðü, âûïîëíèì íåîáõîäèìûå äåéñòâèÿ â êîíòðîëëåðå:

    180

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // src/Acme/TaskBundle/Controller/DefaultController.php
    namespace Acme\TaskBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Acme\TaskBundle\Entity\Task;
    use Symfony\Component\HttpFoundation\Request;
    class DefaultController extends Controller
    {
    public function newAction(Request $request)
    {
    // ñîçäà¼ì çàäà÷ó è ïðèñâàèâàåì åé íåêîòîðûå íà÷àëüíûå äàííûå äëÿ ïðèìåðà
    $task = new Task();
    $task->setTask('Write a blog post');
    $task->setDueDate(new \DateTime('tomorrow'));
    $form = $this->createFormBuilder($task)
    ->add('task', 'text')
    ->add('dueDate', 'date')
    ->getForm();

    }

    }

    return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
    'form' => $form->createView(),
    ));

    Ñîâåò: Ýòîò ïðèìåð ïîêàçûâàåò, êàê ñîçäàòü âàøó ôîðìó íåïîñðåäñòâåííî â êîäå âà-

    øåãî êîíòðîëëåðà. Ïîçäíåå, â ñåêöèè Ñîçäàíèå êëàññîâ ôîðì , âû òàêæå óçíàåòå, êàê
    ñîçäàâàòü ôîðìû â îòäåëüíûõ êëàññàõ, ÷òî ÿâëÿåòñÿ áîëåå ïðåäïî÷òèòåëüíûì âàðèàíòîì è ñäåëàåò âàøè ôîðìû äîñòóïíûìè äëÿ ïîâòîðíîãî èñïîëüçîâàíèÿ.
    Ñîçäàíèå ôîðìû òðåáóåò ñîâñåì íåìíîãî êîäà, òàê êàê îáúåêòû ôîðì â Symfony2 ñîçäàþòñÿ ïðè ïîìîùè êîíñòðóêòîðà ôîðì - form builder. Öåëü êîíñòðóêòîðà ôîðì îáëåã÷èòü íàñêîëüêî ýòî âîçìîæíî ñîçäàíèå ôîðì, âûïîëíÿÿ âñþ òÿæ¼ëóþ ðàáîòó.
    Â ýòîì ïðèìåðå âû äîáàâèëè äâà ïîëÿ â âàøó ôîðìó - task è dueDate, ñîîòâåòñòâóþùèå
    ïîëÿì task è dueDate êëàññà Task. Âû òàêæå óêàçàëè êàæäîìó ïîëþ èõ òèïû (íàïðèìåð
    text, date), êîòîðûå â ÷èñëå ïðî÷èõ ïàðàìåòðîâ, îïðåäåëÿþò - êàêîé HTML òàã áóäåò
    îòîáðàæåí äëÿ ýòîãî ïîëÿ â ôîðìå.
    Symfony2 âêëþ÷àåò ìíîãî âñòðîåííûõ òèïîâ, êîòîðûå áóäóò îáñóæäàòüñÿ ñîâñåì ñêîðî
    (ñì. Âñòðîåííûå òèïû ïîëåé ).

    2.11.

    Ôîðìû

    181

    Symfony Documentation, Âûïóñê 2.0

    Îòîáðàæåíèå ôîðìû

    Òåïåðü, êîãäà ôîðìà ñîçäàíà, ñëåäóþùèì øàãîì áóäåò å¼ îòîáðàæåíèå. Îòîáðàçèòü
    ôîðìó ìîæíî, ïåðåäàâ ñïåöèàëüíûé îáúåêò form view â âàø øàáëîí (îáðàòèòå âíèìàíèå íà êîíñòðóêöèþ $form->createView() â êîíòðîëëåðå âûøå) è èñïîëüçîâàòü ðÿä
    ôóíêöèé-ïîìîùíèêîâ â øàáëîíå:
    ˆ

    Twig

    {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}

    {{ form_widget(form) }}



    ˆ

    PHP



    widget($form) ?>



    Ïðèìå÷àíèå:  ýòîì ïðèìåðå ïðåäïîëàãàåòñÿ, ÷òî âû ñîçäàëè ìàðøðóò task_new,
    êîòîðûé óêàçûâàåò íà êîíòðîëëåð AcmeTaskBundle:Default:new, êîòîðûé áûë ñîçäàí
    ðàíåå.

    Âîò è âñ¼! Íàïå÷àòàâ form_widget(form), êàæäîå ïîëå ôîðìû áóäåò îòîáðàæåíî, òàê
    æå êàê ìåòêè ïîëåé è îøèáêè (åñëè îíè åñòü). Ýòî î÷åíü ïðîñòî, íî íå î÷åíü ãèáêî
    (ïîêà ÷òî). Íà ïðàêòèêå âàì, ñêîðåå âñåãî, çàõî÷åòñÿ îòîáðàçèòü êàæäîå ïîëå ôîðìû
    îòäåëüíî, ÷òîáû èìåòü ïîëíûé êîíòðîëü íàä òåì êàê ôîðìà âûãëÿäèò. Âû óçíàåòå, êàê
    ñäåëàòü ýòî â ñåêöèè  Îòîáðàæåíèå ôîðìû â øàáëîíå .

    182

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïðåæäå ÷åì äâèãàòüñÿ äàëüøå, îáðàòèòå âíèìàíèå íà òî, êàê áûëî îòîáðàæåíî ïîëå
    task, ñîäåðæàùåå çíà÷åíèå ïîëÿ task îáúåêòà $task (Write a blog post). Ýòî - ïåðâàÿ
    çàäà÷à ôîðì: ïîëó÷èòü äàííûå îò îáúåêòà è ïåðåâåñòè èõ â ôîðìàò, ïîäõîäÿùèé äëÿ
    èõ ïîñëåäóþùåãî îòîáðàæåíèÿ â HTML ôîðìå.

    Ñîâåò: Ñèñòåìà ôîðì äîñòàòî÷íî óìíà, ÷òîáû ïîëó÷èòü äîñòóï ê çíà÷åíèþ çàùè-

    ù¼ííîãî (protected) ïîëÿ task ÷åðåç ìåòîäû getTask() è setTask() êëàññà Task. Òàê
    êàê ïîëå íå ïóáëè÷íîå (public), îíî äîëæíî èìåòü ãåòòåð è ñåòòåð ìåòîäû äëÿ òîãî,
    ÷òîáû êîìïîíåíò ôîðì ìîã ïîëó÷èòü äàííûå èç ýòîãî ïîëÿ è èçìåíèòü èõ. Äëÿ áóëåâûõ ïîëåé âû òàêæå ìîæåòå èñïîëüçîâàòü is* ìåòîä (íàïðèìåð isPublished()) âìåñòî
    getPublished().

    Îáðàáîòêà îòïðàâêè ôîðì

    Âòîðîé îáÿçàííîñòüþ ôîðì ÿâëÿåòñÿ ïåðåâîä äàííûõ, îòïðàâëåííûõ ïîëüçîâàòåëåì, â
    ñâîéñòâà îáúåêòà. Äëÿ òîãî ÷òîáû ýòî ïðîèçîøëî, îòïðàâëåííûå äàííûå äîëæíû áûòü
    ïðèâÿçàíû ê ôîðìå. Äîáàâüòå â êîíòðîëëåð ñëåäóþùèå ñòðîêè:
    // ...
    public function newAction(Request $request)
    {
    // ñîçäà¼ì íîâûé îáúåêò $task (áåç äàííûõ ïî óìîë÷àíèþ)
    $task = new Task();
    $form = $this->createFormBuilder($task)
    ->add('task', 'text')
    ->add('dueDate', 'date')
    ->getForm();
    if ($request->getMethod() == 'POST') {
    $form->bindRequest($request);
    if ($form->isValid()) {
    // âûïîëíÿåì ïðî÷èå äåéñòâèå, íàïðèìåð, ñîõðàíÿåì çàäà÷ó â áàçå äàííûõ

    }
    }

    }

    return $this->redirect($this->generateUrl('task_success'));

    // ...

    Òåïåðü, ïðè îòïðàâêå ôîðìû êîíòðîëëåð ïðèâÿçûâàåò îòïðàâëåííûå äàííûå ê ôîð2.11.

    Ôîðìû

    183

    Symfony Documentation, Âûïóñê 2.0

    ìå, êîòîðàÿ ïðèñâàèâàåò ýòè äàííûå ïîëÿì task è dueDate îáúåêòà $task. Ýòà çàäà÷à
    âûïîëíÿåòñÿ ìåòîäîì bindRequest().

    Ïðèìå÷àíèå: Êàê òîëüêî âûçûâàåòñÿ ìåòîä bindRequest(), îòïðàâëåííûå äàííûå

    òóò æå ïðèñâàèâàþòñÿ ñîîòâåòñòâóþùåìó îáúåêòó ôîðìû. Ýòî ïðîèñõîäèò âíå çàâèñèìîñòè îò òîãî, âàëèäíû ëè ýòè äàííûå èëè íåò.
    Ýòîò êîíòðîëëåð ñëåäóåò òèïè÷íîìó ñöåíàðèþ ïî îáðàáîòêå ôîðì è èìååò òðè âîçìîæíûõ ïóòè:
    1. Ïðè ïåðâè÷íîé çàãðóçêå ñòðàíèöû â áðàóçåð ìåòîä çàïðîñà áóäåò GET, ôîðìà ëèøü
    ñîçäà¼òñÿ è îòîáðàæàåòñÿ;
    2. Êîãäà ïîëüçîâàòåëü îòïðàâëÿåò ôîðìó (ò.å. ìåòîä áóäåò óæå POST) ñ íåâåðíûìè
    äàííûìè (âîïðîñû âàëèäàöèè áóäóò ðàññìîòðåíû íèæå, à ïîêà ïðîñòî ïðåäïîëîæèì ÷òî äàííûå íå âàëèäíû), ôîðìà áóäåò ïðèâÿçàíà ê äàííûì è îòîáðàæåíà
    âìåñòå ñî âñåìè îøèáêàìè âàëèäàöèè;
    3. Êîãäà ïîëüçîâàòåëü îòïðàâëÿåò ôîðìó ñ âàëèäíûìè äàííûìè, ôîðìà áóäåò ïðèâÿçàíà ê äàííûì è ó âàñ åñòü âîçìîæíîñòü äëÿ âûïîëíåíèÿ íåêîòîðûõ äåéñòâèé,
    èñïîëüçóÿ îáúåêò $task (íàïðèìåð ñîõðàíèòü åãî â áàçå äàííûõ) ïåðåä òåì êàê
    ïåðåíàïðàâèòü ïîëüçîâàòåëÿ íà äðóãóþ ñòðàíèöó (íàïðèìåð, thank you èëè
    success).

    Ïðèìå÷àíèå: Ïåðåíàïðàâëåíèå ïîëüçîâàòåëÿ ïîñëå óñïåøíîé îòïðàâêè ôîðìû
    ïðåäîòâðàùàåò ïîâòîðíóþ îòïðàâêó ýòèõ æå äàííûõ, åñëè ïîëüçîâàòåëü îáíîâèò ñòðàíèöó.

    2.11.2 Âàëèäàöèÿ ôîðì

    Â ïðåäûäóùåé ñåêöèè âû óçíàëè, ÷òî ôîðìà ìîæåò áûòü îòïðàâëåíà ñ âàëèäíûìè èëè
    íå âàëèäíûìè äàííûìè. Â Symfony2 âàëèäàöèÿ ïðèìåíÿåòñÿ ê îáúåêòó, ëåæàùåìó â
    îñíîâå ôîðìû (íàïðèìåð, Task). Äðóãèìè ñëîâàìè, âîïðîñ íå â òîì, âàëèäíà ëè ôîðìà,
    à âàëèäåí ëè îáúåêò $task, ïîñëå òîãî êàê ôîðìà ïåðåäàëà åìó îòïðàâëåííûå äàííûå.
    Âûïîëíèâ ìåòîä $form->isValid(), ìîæíî óçíàòü âàëèäíû ëè äàííûå îáúåêòà $task
    èëè æå íåò.
    Âàëèäàöèÿ âûïîëíÿåòñÿ ïîñðåäñòâîì äîáàâëåíèå íàáîðà ïðàâèë (íàçûâàåìûõ îãðàíè÷åíèÿìè) ê êëàññó. Äëÿ òîãî, ÷òîáû óâèäåòü âàëèäàöèþ â äåéñòâèè, äîáàâüòå îãðàíè÷åíèÿ
    äëÿ âàëèäàöèè òîãî, ÷òî ïîëå task íå ìîæåò áûòü ïóñòî, à ïîëå dueDate íå ìîæåò áûòü
    ïóñòî è äîëæíî ñîäåðæàòü îáúåêò \DateTime.
    ˆ

    184

    YAML

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    # Acme/TaskBundle/Resources/config/validation.yml
    Acme\TaskBundle\Entity\Task:
    properties:
    task:
    - NotBlank: ~
    dueDate:
    - NotBlank: ~
    - Type: \DateTime

    ˆ

    Annotations

    // Acme/TaskBundle/Entity/Task.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Task
    {
    /**
    * @Assert\NotBlank()
    */
    public $task;

    }

    ˆ

    /**
    * @Assert\NotBlank()
    * @Assert\Type("\DateTime")
    */
    protected $dueDate;

    XML









    \DateTime




    ˆ

    PHP

    // Acme/TaskBundle/Entity/Task.php
    use Symfony\Component\Validator\Mapping\ClassMetadata;

    2.11.

    Ôîðìû

    185

    Symfony Documentation, Âûïóñê 2.0

    use Symfony\Component\Validator\Constraints\NotBlank;
    use Symfony\Component\Validator\Constraints\Type;
    class Task
    {
    // ...
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('task', new NotBlank());

    }

    }

    $metadata->addPropertyConstraint('dueDate', new NotBlank());
    $metadata->addPropertyConstraint('dueDate', new Type('\DateTime'));

    Ýòî âñ¼! Åñëè âû îòïðàâèòå ôîðìó ñ îøèáî÷íûìè çíà÷åíèÿìè - âû óâèäèòå ÷òî ñîîòâåòñòâóþùèå îøèáêè áóäóò îòîáðàæåíû â ôîðìå.

    HTML5 Âàëèäàöèÿ
    Íà÷èíàÿ ñ HTML5, ìíîãèå áðàóçåðû ìîãóò âûïîëíÿòü íåêîòîðûå âàëèäàöèîííûå
    îãðàíè÷åíèÿ íà ñòîðîíå êëèåíòà, áåç îòïðàâêè ôîðìû. Òèïè÷íûì îãðàíè÷åíèåì
    ÿâëÿåòñÿ óêàçàíèå àòðèáóòà required äëÿ ïîëåé, êîòîðûå áóäóò îáÿçàòåëüíûìè.
    Â áðàóçåðàõ, êîòîðûå ïîääåðæèâàþò HTML5, ýòîò àòðèáóò áóäåò ïîçâîëÿòü îòîáðàæàòü áðàóçåðíîå ñîîáùåíèå îá îøèáêå, åñëè ïîëüçîâàòåëü ïîïðîáóåò îòïðàâèòü
    ôîðìó ñ ïóñòûì ñîîòâåòñòâóþùèì ïîëåì.
    Ãåíåðèðîâàííûå ôîðìû ïîëíîñòüþ ïîääåðæèâàþò ýòó âîçìîæíîñòü, äîáàâëÿÿ ñîîòâåòñòâóþùèå HTML-àòðèáóòû, êîòîðûå àêòèâèðóþò HTML5 êëèåíòñêóþ âàëèäàöèþ. Òåì íå ìåíåå, âàëèäàöèÿ íà ñòîðîíå êëèåíòà ìîæåò áûòü îòêëþ÷åíà ïóò¼ì
    äîáàâëåíèÿ àòðèáóòà novalidate ê òàãó form èëè formnovalidate ê òàãó submit.
    Ýòî áûâàåò íåîáõîäèìî, êîãäà âàì íóæíî ïðîòåñòèðîâàòü âàøè ñåðâåðíûå îãðàíè÷åíèÿ, íî, ê ïðèìåðó, áðàóçåð íå äà¼ò îòïðàâèòü ôîðìó ñ ïóñòûìè ïîëÿìè.
    Âàëèäàöèÿ - ýòî âàæíàÿ ôóíêöèÿ â ñîñòàâå Symfony2, îíà îïèñûâàåòñÿ â
    ãëàâå .

    îòäåëüíîé

    Âàëèäàöèîííûå ãðóïïû

    Ñîâåò: Åñëè âû íå èñïîëüçóåòå
    ñåêöèþ.

    âàëèäàöèîííûå ãðóïïû ,

    Åñëè âàø îáúåêò èñïîëüçóåò âîçìîæíîñòè
    186

    âû ìîæåòå ïðîïóñòèòü ýòó

    âàëèäàöèîííûõ ãðóïï ,

    âàì íóæíî óêàçàòü,
    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    êàêèå ãðóïïû âû õîòèòå èñïîëüçîâàòü:
    // ...
    $form = $this->createFormBuilder($users, array(
    'validation_groups' => array('registration'),
    ))->add(...)
    ;

    Åñëè âû ñîçäà¼òå êëàññû ôîðì (õîðîøàÿ ïðàêòèêà), òîãäà âàì íóæíî óêàçàòü ñëåäóþùèé êîä â ìåòîä getDefaultOptions():
    // ...
    public function getDefaultOptions(array $options)
    {
    return array(
    'validation_groups' => array('registration')
    );
    }

    Â îáîèõ ýòèõ ïðèìåðàõ, äëÿ âàëèäàöèè îáúåêòà, äëÿ êîòîðîãî ñîçäàíà ôîðìà, áóäåò
    èñïîëüçîâàíà ëèøü ãðóïïà registration.
    Groups based on Submitted Data

    Äîáàâëåíî â âåðñèè 2.1: The ability to specify a callback or Closure in validation_groups
    is new to version 2.1 Åñëè âàì òðåáóåòñÿ äîïîëíèòåëüíàÿ ëîãèêà äëÿ îïðåäåëåíèÿ âàëèäàöèîííûõ ãðóïï, íàïðèìåð, íà ñîâíîâàíèè äàííûõ, îòïðàâëåííûõ ïîëüçîâàòåëåì, âû
    ìîæåòå óñòàíîâèòü çíà÷åíèåì îïöèè validation_groups â ìàññèâ ñ callback èëè çàìûêàíèå (Closure).

    public function getDefaultOptions(array $options)
    {
    return array(
    'validation_groups' => array('Acme\\AcmeBundle\\Entity\\Client', 'determineValidationGro
    );
    }

    Ýòîò êîä âûçîâåò ñòàòè÷åñêèé ìåòîä determineValidationGroups() êëàññà Client ñ òåêóùåé ôîðìîé â êà÷åñòâå àðãóìåíòà, ïîñëå òîãî êàê äàííûå áóäóò ïðèâÿçàíû (bind) ê
    ôîðìå, íî ïåðåä çàïóñêîì ïðîöåññà âàëèäàöèè. Âû òàêæå ìîæåòå îïðåäåëèòü ëîãèêó â
    çàìûêàíèè Closure, íàïðèìåð:

    2.11.

    Ôîðìû

    187

    Symfony Documentation, Âûïóñê 2.0

    public function getDefaultOptions(array $options)
    {
    return array(
    'validation_groups' => function(FormInterface $form) {
    $data = $form->getData();
    if (Entity\Client::TYPE_PERSON == $data->getType()) {
    return array('person')
    } else {
    return array('company');
    }
    },
    );
    }

    2.11.3 Âñòðîåííûå òèïû ïîëåé

     ñîñòàâ Symfony âõîäèò áîëüøîå ÷èñëî òèïîâ ïîëåé, êîòîðûå ïîêðûâàþò âñå òèïè÷íûå
    ïîëÿ è òèïû äàííûõ, ñ êîòîðûìè âû ñòîëêí¼òåñü:
    Òåêñòîâûå ïîëÿ

    ˆ text
    ˆ textarea
    ˆ email
    ˆ integer
    ˆ money
    ˆ number
    ˆ password
    ˆ percent
    ˆ search
    ˆ url
    Ïîëÿ äëÿ âûáîðà

    ˆ choice
    ˆ entity

    188

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ country
    ˆ language
    ˆ locale
    ˆ timezone
    Ïîëÿ äëÿ äàòû è âðåìåíè

    ˆ

    date

    ˆ datetime
    ˆ time
    ˆ birthday
    Ïðî÷èå ïîëÿ

    ˆ checkbox
    ˆ file
    ˆ radio
    Ãðóïïû ïîëåé

    ˆ

    collection

    ˆ repeated
    Ñêðûòûå ïîëÿ

    ˆ hidden
    ˆ csrf
    Áàçîâûå ïîëÿ

    ˆ field
    ˆ form
    Âû òàêæå ìîæåòå ñîçäàâàòü ñâîè ñîáñòâåííûå òèïû ïîëåé. Ýòà âîçìîæíîñòü ïîäðîáíî
    îïèñûâàåòñÿ â ñòàòüå êíèãè ðåöåïòîâ  /cookbook/form/create_custom_field_type.

    2.11.

    Ôîðìû

    189

    Symfony Documentation, Âûïóñê 2.0

    Îïöèè ïîëåé ôîðì

    Êàæäûé òèï ïîëÿ èìååò íåêîòîðîå ÷èñëî îïöèé, êîòîðûå ìîæíî èñïîëüçîâàòü äëÿ èõ
    íàñòðîéêè. Íàïðèìåð, ïîëå dueDate ñåé÷àñ îòîáðàæàåò 3 ñåëåêòáîêñà. Òåì íå ìåíåå,
    ïîëå date ìîæíî íàñòðîèòü òàêèì îáðàçîì, ÷òîáû îòîáðàæàëñÿ îäèí òåêñòáîêñ (ãäå
    ïîëüçîâàòåëü ñìîæåò ââåñòè äàòó â âèäå ñòðîêè):
    // ...
    ->add('dueDate', 'date', array('widget' => 'single_text'))

    Âñå òèïû ïîëåé èìåþò ìíîãî ðàçëè÷íûõ îïöèé, êîòîðûå ìîãóò áûòü äëÿ íèõ óêàçàíû.
    Ìíîãèå èç ýòèõ îïöèé - ñïåöèôè÷íû äëÿ êîíêðåòíîãî òèïà ïîëÿ è áîëåå ïîäðîáíóþ
    èíôîðìàöèþ î íèõ ìîæíî ïîëó÷èòü èç ñïðàâî÷íèêà.

    Îïöèÿ required
    Íàèáîëåå òèïè÷íîé îïöèåé ÿâëÿåòñÿ îïöèÿ required, êîòîðàÿ ìîæåò áûòü óêàçàíà äëÿ ëþáîãî ïîëÿ. Ïî óìîë÷àíèþ, îïöèÿ required óñòàíîâëåíà â true, ÷òî äà¼ò
    âîçìîæíîñòü áðàóçåðàì ñ ïîääåðæêîé HTML5 èñïîëüçîâàòü âñòðîåííóþ â íèõ êëèåíòñêóþ âàëèäàöèþ, åñëè ïîëå îñòà¼òñÿ ïóñòûì. Åñëè âàì ýòîãî íå òðåáóåòñÿ, èëè
    æå óñòàíîâèòå îïöèþ required â false èëè æå îòêëþ÷èòå âàëèäàöèþ HTML5 .
    Îòìåòèì òàêæå, ÷òî óñòàíîâêà îïöèè required â true íå âëèÿåò íà ñåðâåðíóþ
    âàëèäàöèþ. Äðóãèìè ñëîâàìè, åñëè ïîëüçîâàòåëü îòïðàâëÿåò ïóñòîå çíà÷åíèå äëÿ
    ýòîãî ïîëÿ (ïðè ïîìîùè ñòàðîãî áðàóçåðà èëè âåá-ñåðâèñà) îíî áóäåò ñ÷èòàòüñÿ
    âàëèäíûì, åñëè âû íå èñïîëüçóåòå îãðàíè÷åíèÿ NotBlank èëè NotNull.
    Òàêèì îáðàçîì, îïöèÿ required - õîðîøà, íî ñåðâåðíóþ âàëèäàöèþ èñïîëüçîâàòü
    íåîáõîäèìî âñåãäà.

    2.11.4 Àâòîìàòè÷åñêîå îïðåäåëåíèå òèïîâ ïîëåé

    Òåïåðü, êîãäà âû äîáàâèëè äàííûå äëÿ âàëèäàöèè â êëàññ Task, Symfony òåïåðü ìíîãî
    çíàåò î âàøèõ ïîëÿõ. Åñëè âû ïîçâîëèòå, Symfony ìîæåò îïðåäåëÿòü (óãàäûâàòü)
    òèï âàøåãî ïîëÿ è óñòàíàâëèâàòü åãî àâòîìàòè÷åñêè.  ýòîì ïðèìåðå, Symfony ìîæåò
    îïðåäåëèòü ïî ïðàâèëàì âàëèäàöèè, ÷òî task ÿâëÿåòñÿ ïîëåì òèïà text è dueDate ïîëåì òèïà date:
    public function newAction()

    190

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    {

    }

    $task = new Task();
    $form = $this->createFormBuilder($task)
    ->add('task')
    ->add('dueDate', null, array('widget' => 'single_text'))
    ->getForm();

    Àâòîìàòè÷åñêîå îïðåäåëåíèå àêòèâèðóåòñÿ, êîãäà âû îïóñêàåòå âòîðîé àðãóìåíò â ìåòîäå add() (èëè åñëè âû óêàçûâàåòå null äëÿ íåãî). Åñëè âû ïåðåäà¼òå ìàññèâ îïöèé â
    êà÷åñòâå òðåòüåãî àðãóìåíòà (êàê â ñëó÷àå dueDate âûøå), ýòè îïöèè ïðèìåíÿþòñÿ ê
    óãàäàííîìó ïîëþ.

    Îñòîðîæíî: Åñëè âàøà ôîðìà èñïîëüçóåò îñîáóþ ãðóïïó âàëèäàöèè, îïðåäåëè-

    òåëü òèïîâ ïîëåé áóäåò ó÷èòûâàòü âñå îãðàíè÷åíèÿ ïðè îïðåäåëåíèè òèïîâ ïîëåé
    (âêëþ÷àÿ îãðàíè÷åíèÿ, êîòîðûå íå ÿâëÿþòñÿ ÷àñòüþ èñïîëüçóåìûõ âàëèäàöèîííûõ
    ãðóïï).

    Àâòîìàòè÷åñêîå îïðåäåëåíèå îïöèé äëÿ ïîëåé

    Â äîïîëíåíèå ê îïðåäåëåíèþ òèïà ïîëÿ, Symfony òàêæå ìîæåò ïîïûòàòüñÿ îïðåäåëèòü
    çíà÷åíèÿ îïöèé äëÿ ïîëÿ.

    Ñîâåò: Êîãäà ýòè îïöèè áóäóò óñòàíîâëåíû, ïîëå áóäåò îòîáðàæåíî ñ èñïîëüçîâàíèåì

    îñîáûõ HTML àòðèáóòîâ, êîòîðûå ïîçâîëÿþò âûïîëíÿòü HTML5 âàëèäàöèþ íà ñòîðîíå
    êëèåíòà (íàïðèìåð Assert\MaxLength). È, ïîñêîëüêó âàì íóæíî áóäåò âðó÷íóþ äîáàâëÿòü ïðàâèëà âàëèäàöèè íà ñòîðîíå ñåðâåðà, ýòè îïöèè ìîãóò áûòü óãàäàíû èñõîäÿ èç
    îãðàíè÷åíèé, êîòîðûå âû áóäåòå èñïîëüçîâàòü äëÿ íå¼.
    ˆ required: Îïöèÿ required ìîæåò áûòü îïðåäåëåíà èñõîäÿ èç ïðàâèë âàëèäàöèè
    (ò.å. åñëè ïîëå NotBlank èëè NotNull) èëè æå íà îñíîâàíèè ìåòàäàííûõ Doctrine
    (ò.å. åñëè ïîëå nullable). Ýòî ìîæåò áûòü î÷åíü óäîáíî, òàê êàê ïðàâèëà êëèåíòñêîé âàëèäàöèè àâòîìàòè÷åñêè ñîîòâåòñòâóþò ïðàâèëàì ñåðâåðíîé âàëèäàöèè.
    ˆ min_length: Åñëè ïîëå ÿâëÿåòñÿ îäíèì èç âèäîâ òåêñòîâûõ ïîëåé, îïöèÿ
    min_length ìîæåò áûòü óãàäàíà èñõîäÿ èç ïðàâèë âàëèäàöèè ( åñëè èñïîëüçóþòñÿ
    îãðàíè÷åíèÿ MinLength èëè Min) èëè æå èç ìåòàäàííûõ Doctrine (îñíîâûâàÿñü íà
    äëèíå ïîëÿ).
    ˆ max_length: Àíàëîãè÷íî min_length ñ òîé ëèøü ðàçíèöåé, ÷òî îïðåäåëÿåò ìàêñèìàëüíîå çíà÷åíèå äëèíû ïîëÿ.

    2.11.

    Ôîðìû

    191

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Ýòè îïöèè ìîãóò áûòü îïðåäåëåíû àâòîìàòè÷åñêè, òîëüêî åñëè âû èñïîëüçóåòå àâòîîïðåäåëåíèå ïîëåé (íå óêàçûâàåòå èëè ïåðåäà¼òå null â êà÷åñòâå âòîðîãî
    àðãóìåíòà â ìåòîä add()).

    Åñëè âû õîòèòå èçìåíèòü çíà÷åíèÿ, îïðåäåë¼ííûå àâòîìàòè÷åñêè, âû ìîæåòå ïåðåçàïèñàòü èõ, ïåðåäàâàÿ òðåáóåìûå îïöèè â ìàññèâ îïöèé:
    ->add('task', null, array('min_length' => 4))

    2.11.5 Îòîáðàæåíèå ôîðìû â øàáëîíå

    Ðàíåå âû óâèäåëè, êàê ôîðìó öåëèêîì ìîæíî îòîáðàçèòü ïðè ïîìîùè ëèøü îäíîé ñòðîêè êîäà. Êîíå÷íî æå, íà ïðàêòèêå âàì ïîòðåáóåòñÿ áîëüøàÿ ãèáêîñòü ïðè îòîáðàæåíèè:
    ˆ

    Twig

    {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}

    {{ form_errors(form) }}
    {{ form_row(form.task) }}
    {{ form_row(form.dueDate) }}
    {{ form_rest(form) }}



    ˆ

    PHP



    errors($form) ?>
    row($form['task']) ?>
    row($form['dueDate']) ?>
    rest($form) ?>



    Äàâàéòå áîëåå ïîäðîáíî ðàññìîòðèì êàæäóþ ÷àñòü:
    ˆ form_enctype(form)
    192

    -

    åñëè

    õîòü

    îäíî

    ïîëå

    ôîðìû

    ÿâëÿåòñÿ

    ïîëåì

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    äëÿ

    çàãðóçêè

    ôàéëà,

    ýòà

    ôóíêöèÿ
    enctype="multipart/form-data";

    îòîáðàçèò

    íåîáõîäèìûé

    àòðèáóò

    ˆ form_errors(form) - Îòîáðàæàåò ãëîáàëüíûå ïî îòíîøåíèþ ê ôîðìå öåëèêîì
    îøèáêè âàëèäàöèè (îøèáêè äëÿ ïîëåé îòîáðàæàþòñÿ ïîñëå íèõ);
    ˆ form_row(form.dueDate) - Îòîáðàæàåò òåêñòîâóþ ìåòêó, îøèáêè è HTML-âèäæåò
    äëÿ çàäàííîãî ïîëÿ (íàïðèìåð äëÿ dueDate) âíóòðè div ýëåìåíòà (ïî óìîë÷àíèþ);
    ˆ form_rest(form) - Îòîáðàæàåò âñå îñòàëüíûå ïîëÿ, êîòîðûå åù¼ íå áûëè îòîáðàæåíû. Êàê ïðàâèëî õîðîøàÿ èäåÿ ðàñïîëîæèòü âûçîâ ýòîãî õåëïåðà âíèçó êàæäîé ôîðìû (íà ñëó÷àé åñëè âû çàáûëè âûâåñòè êàêîå-ëèáî ïîëå èëè æå íå õîòèòå
    âðó÷íóþ îòîáðàæàòü ñêðûòûå ïîëÿ). Ýòîò õåëïåð òàêæå óäîáåí äëÿ àêòèâàöèè
    àâòîìàòè÷åñêîé çàùèòû îò CSRF àòàê .
    Îñíîâíàÿ ÷àñòü ðàáîòû ñäåëàíà ïðè ïîìîùè õåëïåðà form_row, êîòîðûé îòîáðàæàåò
    ìåòêó, îøèáêè è âèäæåò äëÿ êàæäîãî ïîëÿ âíóòðè òàãà div. Â ñåêöèè Äèçàéí ôîðì âû
    óçíàåòå, êàê ìîæíî íàñòðîèòü âûâîä form_row íà ðàçëè÷íûõ óðîâíÿõ.

    Ñîâåò:

    Âû ìîæåòå ïîëó÷èòü äîñòóï ê äàííûì âàøåé ôîðìû ïðè ïîìîùè
    form.vars.value:
    ˆ

    Twig

    {{ form.vars.value.task }}

    ˆ

    PHP

    get('value')->getTask() ?>

    Îòîáðàæåíèå êàæäîãî ïîëÿ âðó÷íóþ

    Õåëïåð form_row î÷åíü óäîáåí, òàê êàê âû ìîæåòå áûñòðî îòîáðàçèòü êàæäîå ïîëå âàøåé ôîðìû (è ðàçìåòêà, èñïîëüçóåìàÿ äëÿ êàæäîé ñòðîêè ìîæåò áûòü íàñòðîåíà). Íî,
    òàê êàê æèçíü êàê ïðàâèëî ñëîæíåå, ÷åì íàì õîòåëîñü áû, âû ìîæåòå òàêæå îòîáðàçèòü êàæäîå ïîëå âðó÷íóþ.  êîíå÷íîì èòîãå âû ïîëó÷èòå òîæå ñàìîå ÷òî è ñ õåëïåðîì
    form_row:
    ˆ

    Twig

    {{ form_errors(form) }}

    {{ form_label(form.task) }}
    {{ form_errors(form.task) }}
    {{ form_widget(form.task) }}

    2.11.

    Ôîðìû

    193

    Symfony Documentation, Âûïóñê 2.0


    {{ form_label(form.dueDate) }}
    {{ form_errors(form.dueDate) }}
    {{ form_widget(form.dueDate) }}

    {{ form_rest(form) }}

    ˆ

    PHP

    errors($form) ?>

    label($form['task']) ?>
    errors($form['task']) ?>
    widget($form['task']) ?>


    label($form['dueDate']) ?>
    errors($form['dueDate']) ?>
    widget($form['dueDate']) ?>

    rest($form) ?>

    Åñëè àâòîìàòè÷åñêè ñîçäàííàÿ ìåòêà äëÿ ïîëÿ âàì íå íðàâèòñÿ, âû ìîæåòå óêàçàòü å¼
    ÿâíî:
    ˆ

    Twig

    {{ form_label(form.task, 'Task Description') }}

    ˆ

    PHP

    label($form['task'], 'Task Description') ?>

    Íàêîíåö, íåêîòîðûå òèïû ïîëåé èìåþò äîïîëíèòåëüíûå îïöèè îòîáðàæåíèÿ, êîòîðûå
    ìîæíî óêàçûâàòü âèäæåòó. Ýòè îïöèè äîêóìåíòèðîâàíû äëÿ êàæäîãî òàêîãî òèïà,
    íî îáùåé äëÿ âñåõ îïöèåé ÿâëÿåòñÿ attr, êîòîðàÿ ïîçâîëÿåò âàì ìîäèôèöèðîâàòü
    àòðèáóòû ýëåìåíòà ôîðìû. Ñëåäóþùèé ïðèìåð äîáàâèò òåêñòîâîìó ïîëþ CSS êëàññ
    task_field:
    ˆ

    Twig

    {{ form_widget(form.task, { 'attr': {'class': 'task_field'} }) }}

    ˆ

    194

    PHP

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    widget($form['task'], array(
    'attr' => array('class' => 'task_field'),
    )) ?>

    Ñïðàâî÷íèê ïî ôóíêöèÿì Twig

    Åñëè âû èñïîëüçóåòå Twig, ïîëíàÿ ñïðàâî÷íàÿ èíôîðìàöèÿ î ôóíêöèÿõ, èñïîëüçóåìûõ
    äëÿ îòîáðàæåíèÿ ôîðì, äîñòóïíà â ñïðàâî÷íèêå . Îçíàêîìüòåñü ñ ýòîé èíôîðìàöèåé,
    äëÿ òîãî ÷òîáû óçíàòü áîëüøå î äîñòóïíûõ õåëïåðàõ è îïöèÿõ, êîòîðûå äëÿ íèõ äîñòóïíû.
    2.11.6 Ñîçäàíèå êëàññîâ ôîðì

    Êàê âû óæå âèäåëè ðàíåå, ôîðìà ìîæåò áûòü ñîçäàíà è èñïîëüçîâàíà íåïîñðåäñòâåííî
    â êîíòðîëëåðå. Òåì íå ìåíåå, ëó÷øåé ïðàêòèêîé ÿâëÿåòñÿ ñîçäàíèå ôîðìû â îòäåëüíîì PHP-êëàññå, êîòîðûé ìîæåò áûòü èñïîëüçîâàí ïîâòîðíî â ëþáîì ìåñòå âàøåãî
    ïðèëîæåíèÿ. Ñîçäàéòå íîâûé êëàññ, êîòîðûé áóäåò ñîäåðæàòü ëîãèêó ñîçäàíèÿ ôîðìû
    task:
    // src/Acme/TaskBundle/Form/Type/TaskType.php
    namespace Acme\TaskBundle\Form\Type;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilder;
    class TaskType extends AbstractType
    {
    public function buildForm(FormBuilder $builder, array $options)
    {
    $builder->add('task');
    $builder->add('dueDate', null, array('widget' => 'single_text'));
    }

    }

    public function getName()
    {
    return 'task';
    }

    Ýòîò íîâûé êëàññ ñîäåðæèò âñå íåîáõîäèìûå óêàçàíèÿ äëÿ ñîçäàíèÿ ôîðìû çàäà÷è
    (îáðàòèòå âíèìàíèå, ÷òî ìåòîä getName() äîëæåí âîçâðàùàòü óíèêàëüíûé èäåíòèôèêàòîð äëÿ äàííîé ôîðìû). Òåïåðü, âû ìîæåòå èñïîëüçîâàòü ýòîò êëàññ äëÿ áûñòðîãî
    ñîçäàíèÿ îáúåêòà ôîðìû â êîíòðîëëåðå:
    2.11.

    Ôîðìû

    195

    Symfony Documentation, Âûïóñê 2.0

    // src/Acme/TaskBundle/Controller/DefaultController.php
    // äîáàâüòå use äëÿ êëàññà ôîðìû â íà÷àëå ôàéëà êîíòðîëëåðà
    use Acme\TaskBundle\Form\Type\TaskType;
    public function newAction()
    {
    $task = // ...
    $form = $this->createForm(new TaskType(), $task);
    }

    // ...

    Ðàçìåùåíèå ëîãèêè ôîðìû â îòäåëüíîì êëàññå îçíà÷àåò, ÷òî òåïåðü ôîðìà ìîæåò áûòü
    ëåãêî èñïîëüçîâàíà â äðóãîì ìåñòå ïðèëîæåíèÿ. Ýòî íàèëó÷øèé ñïîñîá äëÿ ñîçäàíèÿ
    ôîðì, íî âûáîð êîíå÷íî æå çà âàìè.

    Íàñòðîéêà data_class äëÿ ôîðìû
    Êàæäàÿ ôîðìà äîëæíà çíàòü èìÿ êëàññà, êîòîðûé áóäåò ñîäåðæàòü äàííûå äëÿ
    íå¼ (íàïðèìåð, Acme\TaskBundle\Entity\Task). Êàê ïðàâèëî, ýòè äàííûå îïðåäåëÿþòñÿ àâòîìàòè÷åñêè ïî îáúåêòó, êîòîðûé ïåðåäà¼òñÿ âòîðûì ïàðàìåòðîì â
    ìåòîä createForm (ò.å. $task). Ïîçäíåå, êîãäà âû çàéì¼òåñü âñòðàèâàíèåì ôîðì,
    ïîëàãàòüñÿ íà àâòîîïðåëåíèå óæå áóäåò íåëüçÿ. Òàêèì îáðàçîì, õîòü è íå âñåãäà
    íåîáõîäèìî, íî âñ¼ æå æåëàòåëüíî ÿâíî óêàçûâàòü îïöèþ data_class, äîáàâèâ ñëåäóþùèå ñòðîêè â êëàññ ôîðìû:
    public function getDefaultOptions(array $options)
    {
    return array(
    'data_class' => 'Acme\TaskBundle\Entity\Task',
    );
    }

    2.11.7 Ôîðìû è Doctrine

    Öåëü ëþáîé ôîðìû - ïðåîáðàçîâàíèå äàííûõ èç îáúåêòà (â íàøåì ñëó÷àå Task) â HTML
    ôîðìó è íàîáîðîò - ïðåîáðàçîâàíèå äàííûõ, îòïðàâëåííûõ ïîëüçîâàòåëåì, îáðàòíî â
    îáúåêò. Ïî ñóùåñòâó, òåìà ïî ñîõðàíåíèþ îáúåêòà Task â áàçå äàííûõ ñîâåðøåííî íå
    îòíîñèòñÿ òåìå, îáñóæäàåìîé â ãëàâå Ôîðìû. Òåì íå ìåíåå, åñëè âû ñêîíôèãóðèðîâàëè
    êëàññ Task äëÿ ðàáîòû ñ Doctrine (ò.å. âû äîáàâèëè ìåòàäàííûå äëÿ îòîáðàæåíèÿ
    (mapping metadata) äëÿ íåãî), åãî ñîõðàíåíèå ïîñëå îòïðàâêè ôîðìû ìîæíî âûïîëíèòü
    196

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    â ñëó÷àå, åñëè ôîðìà âàëèäíà:
    if ($form->isValid()) {
    $em = $this->getDoctrine()->getEntityManager();
    $em->persist($task);
    $em->flush();
    }

    return $this->redirect($this->generateUrl('task_success'));

    Åñëè, ïî êàêèì-òî ïðè÷èíàì ó âàñ íåò èçíà÷àëüíîãî îáúåêòà $task, âû ìîæåòå ïîëó÷èòü
    åãî èç ôîðìû:
    $task = $form->getData();

    Áîëüøå èíôîðìàöèè ïî ðàáîòå ñ áàçàìè äàííûõ âû ìîæåòå ïîëó÷èòü â ãëàâå
    ORM .

    Doctrine

    Ñàìîå ãëàâíîå, ÷òî òðåáóåòñÿ óÿñíèòü, êîãäà ôîðìà è äàííûå ñâÿçûâàþòñÿ, äàííûå
    òóò æå ïåðåäàþòñÿ â îáúåêò, ëåæàùèé â îñíîâå ôîðìû. Åñëè âû õîòèòå ñîõðàíèòü ýòè
    äàííûå, âàì íóæíî ïðîñòî ñîõðàíèòü îáúåêò (êîòîðûé óæå ñîäåðæèò îòïðàâëåííûå
    äàííûå).
    2.11.8 Âñòðîåííûå ôîðìû

    Çà÷àñòóþ, êîãäà âû õîòèòå ñîçäàòü ôîðìó, âàì òðåáóåòñÿ äîáàâëÿòü â íå¼ ïîëÿ èç ðàçëè÷íûõ îáúåêòîâ. Íàïðèìåð, ôîðìà ðåãèñòðàöèè ìîæåò ñîäåðæàòü äàííûå, îòíîñÿùèåñÿ ê îáúåêòó User è ê íåñêîëüêèì îáúåêòàì Address. Ê ñ÷àñòüþ, ñ èñïîëüçîâàíèåì
    êîìïîíåíòà ôîðì ñäåëàòü ýòî ëåãêî è åñòåñòâåííî.
    Âñòðàèâàíèå îäíîãî îáúåêòà

    Ïðåäïîëîæèì, ÷òî êàæäàÿ çàäà÷à Task ñîîòâåòñòâóåò íåêîòîðîìó îáúåêòó Category.
    Íà÷í¼ì êîíå÷íî æå ñ ñîçäàíèÿ êëàññà Category:
    // src/Acme/TaskBundle/Entity/Category.php
    namespace Acme\TaskBundle\Entity;
    use Symfony\Component\Validator\Constraints as Assert;
    class Category
    {
    /**
    * @Assert\NotBlank()
    2.11.

    Ôîðìû

    197

    Symfony Documentation, Âûïóñê 2.0

    }

    */
    public $name;

    Çàòåì ñîçäàäèì ñâîéñòâî category â êëàññå Task:
    // ...
    class Task
    {
    // ...
    /**
    * @Assert\Type(type="Acme\TaskBundle\Entity\Category")
    */
    protected $category;
    // ...
    public function getCategory()
    {
    return $this->category;
    }

    }

    public function setCategory(Category $category = null)
    {
    $this->category = $category;
    }

    Òåïåðü âàøå ïðèëîæåíèå íóæíî ïîäïðàâèòü ñ ó÷¼òîì íîâûõ òðåáîâàíèé. Ñîçäàéòå êëàññ
    ôîðìû äëÿ èçìåíåíèÿ îáúåêòà Category:
    // src/Acme/TaskBundle/Form/Type/CategoryType.php
    namespace Acme\TaskBundle\Form\Type;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilder;
    class CategoryType extends AbstractType
    {
    public function buildForm(FormBuilder $builder, array $options)
    {
    $builder->add('name');
    }

    198

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    public function getDefaultOptions(array $options)
    {
    return array(
    'data_class' => 'Acme\TaskBundle\Entity\Category',
    );
    }

    }

    public function getName()
    {
    return 'category';
    }

    Êîíå÷íî öåëüþ æå ÿâëÿåòñÿ èçìåíåíèå Category äëÿ Task íåïîñðåäñòâåííî èç çàäà÷è.
    Äëÿ òîãî ÷òîáû âûïîëíèòü ýòî, äîáàâüòå ïîëå category â ôîðìó TaskType, êîòîðîå
    áóäåò ïðåäñòàâëåíî ýêçåìïëÿðîì íîâîãî êëàññà CategoryType:
    public function buildForm(FormBuilder $builder, array $options)
    {
    // ...
    }

    $builder->add('category', new CategoryType());

    Ïîëÿ ôîðìû CategoryType òåïåðü ìîãóò áûòü îòîáðàæåíû ïðÿìî â ôîðìå TaskType.
    Îòîáðàçèòå ïîëÿ Category òåì æå ñïîñîáîì êàê è ïîëÿ Task:
    ˆ

    Twig

    {# ... #}

    Category



    {{ form_row(form.category.name) }}

    {{ form_rest(form) }}
    {# ... #}

    ˆ

    PHP


    Category



    row($form['category']['name']) ?>


    2.11.

    Ôîðìû

    199

    Symfony Documentation, Âûïóñê 2.0

    rest($form) ?>


    Êîãäà ïîëüçîâàòåëü îòïðàâëÿåò ôîðìó, äàííûå äëÿ ïîëåé Category áóäóò èñïîëüçîâàíû
    äëÿ ñîçäàíèÿ ýêçåìïëÿðà Category, êîòîðûé áóäåò ïðèñâîåí ïîëþ category îáúåêòà
    Task.
    Îáúåêò Category äîñòóïåí ÷åðåç ìåòîä $task->getCategory() è ìîæåò áûòü ñîõðàí¼í
    â áàçó äàííûõ èëè èñïîëüçîâàí ãäå òðåáóåòñÿ.
    Âñòðàèâàíèå êîëëåêöèé ôîðì

    Âû òàêæå ìîæåòå âñòðîèòü â âàøó ôîðìó öåëóþ êîëëåêöèþ ôîðì (íàïðèìåð ôîðìà
    Category ñ ìíîæåñòâîì ñàá-ôîðì Product). Ýòîãî ìîæíî äîñòè÷ü ïðè èñïîëüçîâàíèè
    ïîëÿ collection.
    Ïîäðîáíåå ýòîò òèï ïîëÿ îïèñàí â êíèãå ðåöåïòîâ  /cookbook/form/form_collections
    è ñïðàâî÷íèêå: collection .
    2.11.9 Äèçàéí ôîðì

    Êàæäàÿ ÷àñòü îòîáðàæåíèÿ ôîðìû ìîæåò áûòü íàñòðîåíà â ñîîòâåòñòâèè ñ âàøèìè
    òðåáîâàíèÿìè. Âû ìîæåòå èçìåíèòü êàê îòîáðàæàåòñÿ êàæäàÿ ñòðîêà ôîðìû, èçìåíèòü ðàçìåòêó îòîáðàæåíèÿ îøèáîê è äàæå íàñòðîèòü êàê äîëæåí îòîáðàæàòüñÿ òàã
    textarea. Âû íè÷åì íå îãðàíè÷åíû è ðàçíûå íàñòðîéêè ìîãóò áûòü èñïîëüçîâàíû â
    ðàçíûõ ìåñòàõ.
    Symfony èñïîëüçóåò øàáëîíû äëÿ îòîáðàæåíèÿ âñåõ ÷àñòåé ôîðì, òàêèõ êàê ìåòêè,
    òàãè, input òàãè, ñîîáùåíèÿ îá îøèáêàõ è ìíîãîå äðóãîå.
     Twig êàæäûé òàêîé ôðàãìåíò ïðåäñòàâëåí áëîêîì Twig. Äëÿ íàñòðîéêè ëþáîé ÷àñòè
    îòîáðàæåíèÿ ôîðìû âàì ïðîñòî íàäî çàìåíèòü íóæíûé áëîê.
    Â PHP êàæäûé ôðàãìåíò ôîðìû îòîáðàæàåòñÿ ïîñðåäñòâîì èíäèâèäóàëüíîãî ôàéëà
    øàáëîíà. Äëÿ íàñòðîéêè îòîáðàæåíèÿ ëþáîé ÷àñòè ôîðìû âàì íóæíî çàìåíèòü ñóùåñòâóþùèé øàáëîí íîâûì.
    Äëÿ òîãî ÷òîáû ïîíÿòü, êàê ýòî ðàáîòàåò, äàâàéòå íàñòðîèì îòîáðàæåíèå ôðàãìåíòà
    form_row è äîáàâèì àòðèáóò class äëÿ ýëåìåíòà div, êîòîðûé ñîäåðæèò êàæäóþ ñòðîêó.
    Äëÿ òîãî ÷òîáû âûïîëíèòü ýòî, ñîçäàéòå íîâûé ôàéë øàáëîíà, êîòîðûé áóäåò ñîäåðæàòü íîâóþ ðàçìåòêó:
    ˆ

    Twig

    {# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}
    {% block field_row %}
    200

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    {% spaceless %}

    {{ form_label(form) }}
    {{ form_errors(form) }}
    {{ form_widget(form) }}

    {% endspaceless %}
    {% endblock field_row %}

    ˆ

    PHP



    label($form, $label) ?>
    errors($form) ?>
    widget($form, $parameters) ?>


    Ôðàãìåíò field_row èñïîëüçóåòñÿ ïðè îòîáðàæåíèè áîëüøèíñòâà ïîëåé ïðè ïîìîùè
    ôóíêöèè form_row. Äëÿ òîãî, ÷òîáû ñîîáùèòü êîìïîíåíòó ôîðì, ÷òîáû îí èñïîëüçîâàë
    íîâûé ôðàãìåíò field_row, îïðåäåë¼ííûé âûøå, äîáàâüòå ñëåäóþùóþ ñòðîêó â íà÷àëå
    øàáëîíà, îòîáðàæàþùåãî ôîðìó:
    ˆ

    Twig

    {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
    {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %}


    ˆ

    PHP


    setTheme($form, array('AcmeTaskBundle:Form')) ?>


    Òàã form_theme (â Twig) êàê áû èìïîðòèðóåò ôðàãìåíòû, îïðåäåë¼ííûå â óêàçàííîì
    øàáëîíå è èñïîëüçóåò èõ ïðè îòîáðàæåíèè ôîðìû. Äðóãèìè ñëîâàìè, êîãäà âûçûâàåòñÿ
    ôóíêöèÿ form_row íèæå â ýòîì øàáëîíå, îíà áóäåò èñïîëüçîâàòü áëîê field_row èç
    âàøåé òåìû (âìåñòî áëîêà field_row ïî óìîë÷àíèþ èñïîëüçóåìîãî â Symfony).
    Äëÿ òîãî ÷òîáû íàñòðîèòü ëþáóþ ÷àñòü ôîðìû, âàì âñåãî ëèøü íóæíî ïåðåîïðåäåëèòü
    âñå íåîáõîäèìûå ôðàãìåíòû. Î òîì, êàêèå áëîêè èëè ôàéëû ìîãóò áûòü ïåðåîïðåäåëåíû, ìû ïîãîâîðèì â ñëåäóþùåé ñåêöèè.

    2.11.

    Ôîðìû

    201

    Symfony Documentation, Âûïóñê 2.0

    Äîïîëíèòåëüíóþ èíôîðìàöèþ î êàñòîìèçàöèè ôîðì èùèòå â êíèãå ðåöåïòîâ:
    /cookbook/form/form_customization.
    Èìåíîâàíèå ôðàãìåíòîâ ôîðì

     Symfony, êàæäàÿ îòîáðàæàåìàÿ ÷àñòü ôîðìû - HTML ýëåìåíòû ôîðì, îøèáêè, ìåòêè
    è ò.ä. - îïðåäåëåíû â áàçîâîé òåìå, êîòîðàÿ ïðåäñòàâëÿåò èç ñåáÿ íàáîð áëîêîâ â Twig
    è íàáîð øàáëîíîâ â PHP.
    Â Twig âñå áëîêè îïðåäåëåíû â îäíîì ôàéëå (form_div_layout.html.twig), êîòîðûé ðàñïîëàãàåòñÿ âíóòðè Twig Bridge. Â ýòîì ôàéëå âû ìîæåòå óâèäåòü ëþáîé èç áëîêîâ,
    íåîáõîäèìûõ äëÿ îòîáðàæåíèÿ ëþáîãî ñòàíäàðòíîãî ïîëÿ.
     PHP êàæäûé ôðàãìåíò ðàñïîëîæåí â îòäåëüíîì ôàéëå. Ïî óìîë÷àíèþ, îíè ðàñïîëàãàþòñÿ â äèðåêòîðèè Resources/views/Form â ñîñòàâå ïàêåòà framework (ñì. íà GitHub).
    Íàèìåíîâàíèå êàæäîãî ôðàãìåíòà ñëåäóåò îäíîìó áàçîâîìó ïðàâèëó è ðàçáèòî íà äâå
    ÷àñòè, ðàçäåë¼ííûõ ïîä÷åðêîì (_). Íåñêîëüêî ïðèìåðîâ:
    ˆ field_row - èñïîëüçóåòñÿ ôóíêöèåé form_row äëÿ îòîáðàæåíèÿ áîëüøèíñòâà ïîëåé;
    ˆ textarea_widget - èñïîëüçóåòñÿ ôóíêöèåé form_widget äëÿ îòîáðàæåíèÿ ïîëåé
    òèïà textarea;
    ˆ field_errors - èñïîëüçóåòñÿ ôóíêöèåé form_errors äëÿ îòîáðàæåíèÿ îøèáîê.
    Êàæäûé ôðàãìåíò ïîä÷èíÿåòñÿ ïðîñòîìó ïðàâèëó: type_part. ×àñòü type ñîîòâåòñòâóåò òèïó ïîëÿ, êîòîðîå áóäåò îòîáðàæåíî (íàïðèìåð, textarea, checkbox, date è ò.ä.),
    ÷àñòü part ñîîòâåòñòâóåò æå òîìó, ÷òî èìåííî áóäåò îòîáðàæàòüñÿ (label, widget,
    errors, è ò.ä.). Ïî óìîë÷àíèþ åñòü ÷åòûðå âîçìîæíûõ òèïîâ parts, êîòîðûå îòîáðàæàþòñÿ:

    label
    widget
    errors
    row

    (field_label)
    (field_widget)
    (field_errors)
    (field_row)

    îòîáðàæàåò
    îòîáðàæàåò
    îòîáðàæàåò
    îòîáðàæàåò

    ìåòêó äëÿ ïîëÿ
    HTML-ïðåäñòàâëåíèå äëÿ ïîëÿ
    îøèáêè äëÿ ïîëÿ
    öåëüíóþ ñòðîêó äëÿ ïîëÿ (label, widget & errors)

    Ïðèìå÷àíèå: Åñòü òàêæå åù¼ òðè òèïà

    parts - rows, rest, è enctype - íî çàìåíÿòü
    èõ âàì âðÿä ëè ïîòðåáóåòñÿ, òàê ÷òî è çàáîòèòüñÿ ýòîì íå ñòîèò.

    Çíàÿ òèï ïîëÿ (íàïðèìåð textarea), à òàêæå êàêóþ ÷àñòü âû õîòèòå èçìåíèòü (íàïðèìåð, widget), âû ìîæåòå ñîñòàâèòü èìÿ ôðàãìåíòà, êîòîðûé äîëæåí áûòü ïåðåîïðåäåë¼í (íàïðèìåð, textarea_widget).

    202

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Íàñëåäîâàíèå ôðàãìåíòîâ øàáëîíà ôîðì

     íåêîòîðûõ ñëó÷àÿõ ôðàãìåíò, êîòîðûé âàì íóæíî íàñòðîèòü, íå áóäåò ñóùåñòâîâàòü.
    Íàïðèìåð, â áàçîâîé òåìå íåò ôðàãìåíòà textarea_errors. Íî êàê æå îòîáðàæàþòñÿ
    îøèáêè äëÿ ïîëåé textarea?
    Îòâåò íà ýòîò âîïðîñ òàêîé: îòîáðàæàþòñÿ îíè ïðè ïîìîùè ôðàãìåíòà field_errors.
    Êîãäà Symfony îòîáðàæàåò îøèáêè äëÿ textarea, îí èùåò ôðàãìåíò textarea_errors,
    ïðåæäå ÷åì èñïîëüçîâàòü ñòàíäàðòíûé ôðàãìåíò field_errors. Ëþáîé òèï ïîëÿ èìååò
    ðîäèòåëüñêèé òèï (äëÿ textarea ýòî field) è Symfony èñïîëüçóåò ôðàãìåíò îò ðîäèòåëüñêîãî òèïà, åñëè áàçîâûé ôðàãìåíò íå ñóùåñòâóåò.
    Òàêèì îáðàçîì, ÷òîáû ïåðåîïðåäåëèòü ôðàãìåíò îøèáîê òîëüêî äëÿ ïîëåé textarea,
    ñêîïèðóéòå ôðàãìåíò field_errors, ïåðåèìåíóéòå åãî â textarea_errors è èçìåíèòå
    åãî êàê âàì òðåáóåòñÿ. Äëÿ òîãî, ÷òîáû èçìåíèòü îòîáðàæåíèå îøèáîê äëÿ âñåõ ïîëåé,
    ñêîïèðóéòå è èçìåíèòå ñàì ôðàãìåíò field_errors.

    Ñîâåò: Ðîäèòåëüñêèå òèïû äëÿ âñåõ òèïîâ ïîëåé ìîæíî óçíàòü èç ñïðàâî÷íèêà: òèïû
    ïîëåé .

    Ãëîáàëüíàÿ òåìà äëÿ ôîðì

     ïðèìåðå âûøå âû èñïîëüçîâàëè õåëïåð form_theme (äëÿ Twig), ÷òîáû èìïîðòèðîâàòü èçìåí¼ííûå ôðàãìåíòû ôîðì òîëüêî â îäíó ôîðìó. Âû òàêæå ìîæåòå óêàçàòü
    Symfony òåìó ôîðì äëÿ âñåãî ïðîåêòà â öåëîì.

    Twig
    Äëÿ òîãî, ÷òîáû àâòîìàòè÷åñêè ïîäêëþ÷èòü ïåðåîïðåäåë¼ííûå áëîêè èç ðàíåå ñîçäàííîãî øàáëîíà fields.html.twig, èçìåíèòå âàø ôàéë êîíôèãóðàöèè ñëåäóþùèì îáðàçîì:
    ˆ

    YAML

    # app/config/config.yml
    twig:
    form:
    resources:
    - 'AcmeTaskBundle:Form:fields.html.twig'
    # ...

    ˆ

    2.11.

    XML

    Ôîðìû

    203

    Symfony Documentation, Âûïóñê 2.0




    AcmeTaskBundle:Form:fields.html.twig




    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('twig', array(
    'form' => array('resources' => array(
    'AcmeTaskBundle:Form:fields.html.twig',
    ))
    // ...
    ));

    Ëþáîé áëîê âíóòðè øàáëîíà fields.html.twig áóäåò èñïîëüçîâàí ãëîáàëüíî â ðàìêàõ
    ïðîåêòà äëÿ îïðåäåëåíèÿ ôîðìàòà îòîáðàæåíèÿ ôîðì.

    204

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Íàñòðîéêà îòîáðàæåíèÿ ôîðì â ôàéëå ôîðìû ïðè èñïîëüçîâàíèè Twig
    Ïðè èñïîëüçîâàíèè Twig, âû òàêæå ìîæåòå èçìåíèòü áëîê ôîðìû íåïîñðåäñòâåííî
    âíóòðè øàáëîíà, ãäå òðåáóåòñÿ èçìåíåíèå ñòèëÿ îòîáðàæåíèÿ:
    {% extends '::base.html.twig' %}
    {# import "_self" as the form theme #}
    {% form_theme form _self %}
    {# make the form fragment customization #}
    {% block field_row %}
    {# custom field row output #}
    {% endblock field_row %}
    {% block content %}
    {# ... #}
    {{ form_row(form.task) }}
    {% endblock %}

    Òàã {% form_theme form _self %} ïîçâîëÿåò èçìåíÿòü áëîêè ôîðìû íåïîñðåäñòâåííî âíóòðè òîãî øàáëîíà, êîòîðûé òðåáóåò èçìåíåíèé. Èñïîëüçóéòå ýòîò ìåòîä
    äëÿ áûñòðîé íàñòðîéêè îòîáðàæåíèÿ ôîðìû, åñëè äàííîå èçìåíåíèå íèãäå áîëüøå
    íå ïîòðåáóåòñÿ.

    PHP
    Äëÿ òîãî, ÷òîáû àâòîìàòè÷åñêè ïîäêëþ÷èòü èçìåí¼ííûå øàáëîíû èç äèðåêòîðèè
    Acme/TaskBundle/Resources/views/Form, ñîçäàííîé ðàíåå, äëÿ âñåõ øàáëîíîâ, èçìåíèòå êîíôèãóðàöèþ âàøåãî ïðèëîæåíèÿ ñëåäóþùèì îáðàçîì:
    ˆ

    YAML

    # app/config/config.yml
    framework:
    templating:
    form:
    resources:
    - 'AcmeTaskBundle:Form'
    # ...

    ˆ

    XML



    2.11.

    Ôîðìû

    205

    Symfony Documentation, Âûïóñê 2.0




    AcmeTaskBundle:Form





    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    'templating' => array('form' =>
    array('resources' => array(
    'AcmeTaskBundle:Form',
    )))
    // ...
    ));

    Âñå ôðàãìåíòû, îïðåäåë¼ííûå â äèðåêòîðèè Acme/TaskBundle/Resources/views/Form
    òåïåðü áóäóò èñïîëüçîâàíû âî âñ¼ì ïðèëîæåíèè äëÿ èçìåíåíèÿ ñòèëÿ îòîáðàæåíèÿ
    ôîðì.
    2.11.10 Çàùèòà îò CSRF àòàê

    CSRF - èëè æå Ïîääåëêà ìåæñàéòîâûõ çàïðîñîâ ýòî âèä àòàê, ïîçâîëÿþùèé çëîóìûøëåííèêó âûïîëíÿòü çàïðîñû îò èìåíè ïîëüçîâàòåëåé âàøåãî ïðèëîæåíèÿ, êîòîðûå îíè
    äåëàòü íå ñîáèðàëèñü (íàïðèìåð ïåðåâîä ñðåäñòâ íà ñ÷¼ò õàêåðà). Ê ñ÷àñòüþ, òàêèå
    àòàêè ìîæíî ïðåäîòâðàòèòü, èñïîëüçóÿ CSRF òîêåí â âàøèõ ôîðìàõ.
    Õîðîøèå íîâîñòè! Çàêëþ÷àþòñÿ îíè â òîì, ÷òî Symfony ïî óìîë÷àíèþ äîáàâëÿåò è
    âàëèäèðóåò CSRF òîêåí äëÿ âàñ. Ýòî îçíà÷àåò, ÷òî âû ïîëó÷àåòå çàùèòó îò CSRF
    àòàê íå ïðèëàãàÿ ê ýòîìó íèêàêèõ óñèëèé. Ôàêòè÷åñêè, âñå ôîðìû â ýòîé ãëàâå áûëè
    çàùèùåíû îò ïîäîáíûõ àòàê.
    Çàùèòà îò CSRF àòàê ðàáîòàåò çà ñ÷¼ò äîáàâëåíèÿ â ôîðìû ñêðûòîãî ïîëÿ, íàçûâàåìîãî ïî óìîë÷àíèþ _token, êîòîðîå ñîäåðæèò çíà÷åíèå, êîòîðîå çíàåòå òîëüêî âû è
    ïîëüçîâàòåëü âàøåãî ïðèëîæåíèÿ. Ýòî ãàðàíòèðóåò, ÷òî ïîëüçîâàòåëü - è íèêòî áîëåå îòïðàâèë äàííûå, êîòîðûå ïðèøëè ê âàì. Symfony àâòîìàòè÷åñêè âàëèäèðóåò íàëè÷èå
    è ïðàâèëüíîñòü ýòîãî òîêåíà.
    Ïîëå _token - ýòî ñêðûòîå ïîëå è îíî àâòîìàòè÷åñêè îòîáðàæàåòñÿ, åñëè âû èñïîëüçóåòå
    ôóíêöèþ form_rest() â âàøåì øàáëîíå, êîòîðàÿ îòîáðàæàåò âñå ïîëÿ, êîòîðûå åù¼ íå
    áûëè îòîáðàæåíû â ôîðìå.
    206

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    CSRF òîêåí ìîæíî íàñòðîèòü óðîâíå ôîðìû. Íàïðèìåð:
    class TaskType extends AbstractType
    {
    // ...
    public function getDefaultOptions(array $options)
    {
    return array(
    'data_class'
    => 'Acme\TaskBundle\Entity\Task',
    'csrf_protection' => true,
    'csrf_field_name' => '_token',
    // óíèêàëüíûé êëþ÷ äëÿ ãåíåðàöèè ñåêðåòíîãî òîêåíà
    'intention'
    => 'task_item',
    );
    }
    }

    // ...

    Äëÿ òîãî, ÷òîáû îòêëþ÷èòü CSRF çàùèòó, óñòàíîâèòå îïöèþ csrf_protection â false.
    Íàñòðîéêè òàêæå ìîæíî âûïîëíèòü íà óðîâíå âñåãî ïðîåêòà. Äîïîëíèòåëüíóþ èíôîðìàöèþ ìîæíî íàéòè â ñïðàâî÷íèêå ïî íàñòðîéêå ôîðì.

    Ïðèìå÷àíèå: Îïöèÿ intention (íàìåðåíèå) íå îáÿçàòåëüíà, íî çíà÷èòåëüíî óâåëè÷èâàåò áåçîïàñíîñòü ñãåíåðèðîâàííîãî òîêåíà, äåëàÿ åãî ðàçëè÷íûì äëÿ âñåõ ôîðì.

    2.11.11 Èñïîëüçîâàíèå ôîðì áåç êëàññà

     áîëüøèíñòâå ñëó÷àåâ ôîðìà ïðèâÿçûâàåòñÿ ê îáúåêòó è ïîëÿ ôîðìû ïîëó÷àþò è
    ñîõðàíÿþò äàííûå â ïîëÿ ýòîãî îáúåêòà. Ýòî ðîâíî òî, ñ ÷åì âû ðàáîòàëè ðàíåå â ýòîé
    ãëàâå.
    Òåì íå ìåíåå, âàì âîçìîæíî ïîòðåáóåòñÿ èñïîëüçîâàòü ôîðìó áåç ñîîòâåòñòâóþùåãî
    êëàññà è ïîëó÷àòü ìàññèâ îòïðàâëåííûõ äàííûõ, à íå îáúåêò. Ýòîãî ïðîñòî äîñòè÷ü:
    // óäîñòîâåðüòåñü, ÷òî âû äîáàâèëè use äëÿ ïðîñòðàíñòâà èì¼í Request:
    use Symfony\Component\HttpFoundation\Request
    // ...
    public function contactAction(Request $request)
    {
    $defaultData = array('message' => 'Type your message here');
    $form = $this->createFormBuilder($defaultData)
    2.11.

    Ôîðìû

    207

    Symfony Documentation, Âûïóñê 2.0

    ->add('name', 'text')
    ->add('email', 'email')
    ->add('message', 'textarea')
    ->getForm();
    if ($request->getMethod() == 'POST') {
    $form->bindRequest($request);

    }

    // data is an array with "name", "email", and "message" keys
    $data = $form->getData();

    // ... render the form

    }

    Ïî óìîë÷àíèþ, ôîðìà ïîëàãàåò, ÷òî âû õîòèòå ðàáîòàòü ñ ìàññèâàìè äàííûõ, à íå ñ
    îáúåêòàìè. Åñòü äâà ñïîñîáà èçìåíèòü ýòî ïîâåäåíèå è ñâÿçàòü ôîðìó ñ îáúåêòîì:
    1. Ïåðåäàòü îáúåêò ïðè ñîçäàíèè ôîðìû (ïåðâûé àðãóìåíò createFormBuilder) èëè
    âòîðîé àðãóìåíò createForm);
    2. Îïðåäåëèòü îïöèþ data_class äëÿ âàøåé ôîðìû.
    Åñëè âû ýòîãî íå ñäåëàëè, òîãäà ôîðìà áóäåò âîçâðàùàòü äàííûå â âèäå ìàññèâà. Â
    ýòîì ïðèìåðå, òàê êàê $defaultData íå ÿâëÿåòñÿ îáúåêòîì (è íå óñòàíîâëåíà îïöèÿ
    data_class), $form->getData() â êîíå÷íîì èòîãå âåðí¼ò ìàññèâ.

    Ñîâåò: Âû òàêæå ìîæåòå ïîëó÷èòü äîñòóï ê çíà÷åíèÿì POST (â äàííîì ñëó÷àå name)
    íàïðÿìóþ ÷åðåç îáúåêò çàïðîñà, íàïðèìåð òàê:
    $this->get('request')->request->get('name');

    Òåì íå ìåíåå, â áîëüøèíñòâå ñëó÷àåâ ðåêîìåíäóåòñÿ èñïîëüçîâàòü ìåòîä getData(), òàê
    êàê îí âîçâðàùàåò äàííûå (êàê ïðàâèëî îáúåêò) ïîñëå òîãî êàê îí áûë ïðåîáðàçîâàí
    ôðåéìâîðêîì ôîðì.

    Äîáàâëåíèå âàëèäàöèè

    À êàê æå áûòü ñ âàëèäàöèåé? Îáû÷íî, êîãäà âû èñïîëüçóåòå âûçîâ $form->isValid(),
    îáúåêò âàëèäèðîâàëñÿ íà îñíîâàíèè îãðàíè÷åíèé, êîòîðûå âû äîáàâèëè â ýòîò êëàññ.
    Íî êîãäà êëàññà íåò, êàê äîáàâèòü îãðàíè÷åíèÿ äëÿ äàííûõ èç ôîðìû?
    Îòâåòîì ÿâëÿåòñÿ íàñòðîéêà îãðàíè÷åíèé âðó÷íóþ è ïåðåäà÷à èõ â ôîðìó. Ïîëíîñòüþ
    ýòîò ïîäõîä îïèñàí â ãëàâå î Âàëèäàöèè , ìû æå ðàññìîòðèì ëèøü íåáîëüøîé ïðèìåð:

    208

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // èìïîðò ïðîñòðàíñòâ èì¼í
    use Symfony\Component\Validator\Constraints\Email;
    use Symfony\Component\Validator\Constraints\MinLength;
    use Symfony\Component\Validator\Constraints\Collection;
    $collectionConstraint = new Collection(array(
    'name' => new MinLength(5),
    'email' => new Email(array('message' => 'Invalid email address')),
    ));
    // ñîçäàíèå ôîðìû áåç çíà÷åíèé ïî óìîë÷àíèþ è ñ ÿâíûì óêàçàíèåì îãðàíè÷åíèé äëÿ âàëèäàöèè
    $form = $this->createFormBuilder(null, array(
    'validation_constraint' => $collectionConstraint,
    ))->add('email', 'email')
    // ...
    ;

    Òåïåðü, êîãäà âû âûçûâàåòå $form->isValid(), îãðàíè÷åíèÿ, óêàçàííûå âûøå, âûïîëíÿþòñÿ äëÿ äàííûõ ôîðìû. Åñëè âû èñïîëüçóåòå êëàññ ôîðìû, ïåðåîïðåäåëèòå ìåòîä
    getDefaultOptions:
    namespace Acme\TaskBundle\Form\Type;
    use
    use
    use
    use
    use

    Symfony\Component\Form\AbstractType;
    Symfony\Component\Form\FormBuilder;
    Symfony\Component\Validator\Constraints\Email;
    Symfony\Component\Validator\Constraints\MinLength;
    Symfony\Component\Validator\Constraints\Collection;

    class ContactType extends AbstractType
    {
    // ...
    public function getDefaultOptions(array $options)
    {
    $collectionConstraint = new Collection(array(
    'name' => new MinLength(5),
    'email' => new Email(array('message' => 'Invalid email address')),
    ));

    }

    }

    $options['validation_constraint'] = $collectionConstraint;

    Òåïåðü âû ìîæåòå ñîçäàâàòü ôîðìû ñ âàëèäàöèåé, êîòîðûå âîçâðàùàþò ìàññèâ äàííûõ
    âìåñòî îáúåêòà.  áîëüøèíñòâå ñëó÷àåâ æå ëó÷øå - äà è áîëåå ïðàâèëüíî - ïðèâÿçûâàòü
    2.11.

    Ôîðìû

    209

    Symfony Documentation, Âûïóñê 2.0

    îáúåêòû ê âàøèì ôîðìàì. Íî äëÿ ïðîñòûõ ôîðì âû ìîæåòå ýòîãî è íå äåëàòü.
    2.11.12 Çàêëþ÷åíèå

    Òåïåðü âû çíàåòå âñ¼ íåîáõîäèìîå äëÿ ñîçäàíèÿ ñëîæíûõ ôîðì äëÿ âàøåãî ïðèëîæåíèÿ.
    Ïðè ñîçäàíèè ôîðì, íå çàáûâàéòå ÷òî ïåðâîé öåëüþ ôîðìû ÿâëÿåòñÿ òðàíñëèðîâàíèå
    äàííûõ èç îáúåêòà (Task) â HTML ôîðìó, ÷òîáû ïîëüçîâàòåëü ìîã ìîäèôèöèðîâàòü
    ýòè äàííûå. Âòîðîé öåëüþ ôîðìû ÿâëÿåòñÿ ïîëó÷åíèå îòïðàâëåííûõ ïîëüçîâàòåëåì
    äàííûõ è ïåðåäà÷à èõ îáðàòíî â îáúåêò.
    Åñòü åù¼ ìíîãî âåùåé, êîòîðûå ñòîèò óçíàòü î ïðåêðàñíîì ìèðå ôîðì, òàêèõ êàê
    çàãðóçêà ôàéëîâ ïðè ïîìîùè Doctrine, èëè æå êàê ñîçäàíèå ôîðìû ñ äèíàìè÷åñêè
    ìåíÿåìûì ÷èñëîì âëîæåííûõ ôîðì ( íàïðèìåð, ñïèñîê todo, ãäå âû ìîæåòå äîáàâëÿòü
    íîâûå ïîëÿ ïðè ïîìîùè Javascript ïåðåä îòïðàâêîé). Èùèòå îòâåòû â êíèãå ðåöåïòîâ.
    Òàêæå èçó÷èòå ñïðàâî÷íèê ïî òèïàì ïîëåé , êîòîðûé âêëþ÷àåò ïðèìåðû èñïîëüçîâàíèÿ
    ïîëåé è èõ îïöèé.
    2.11.13 ×èòàéòå òàêæå â êíèãå ðåöåïòîâ

    ˆ /cookbook/doctrine/file_uploads
    ˆ Ðàáîòà ñ ïîëåì File
    ˆ Ñîçäàíèå ïîëüçîâàòåëüñêîãî ïîëÿ
    ˆ /cookbook/form/form_customization
    ˆ /cookbook/form/dynamic_form_generation
    ˆ /cookbook/form/data_transformers

    2.12 Áåçîïàñíîñòü
    Îáåñïå÷åíèå áåçîïàñíîñòè (Security) - ýòî äâóõ øàãîâûé ïðîöåññ, öåëüþ êîòîðîãî ÿâëÿåòñÿ ïðåäîòâðàùåíèå äîñòóïà ïîëüçîâàòåëÿ ê ðåñóðñàì, ïîëó÷èòü êîòîðûå îí íå èìååò
    ïðàâà.
     ïåðâóþ î÷åðåäü, ñèñòåìà áåçîïàñíîñòè èäåíòèôèöèðóåò ïîëüçîâàòåëÿ, çàïðàøèâàÿ ó
    íåãî òó èëè èíóþ èíôîðìàöèþ. Ýòîò ïðîöåññ íàçûâàåòñÿ àóòåíòèôèêàöèåé è îçíà÷àåò, ÷òî ñèñòåìà ïûòàåòñÿ ïîíÿòü, êòî âû åñòü òàêîé.
    Ïîñëå òîãî êàê ñèñòåìà èäåíòèôèöèðóåò âàñ, ñëåäóþùèì øàãîì òðåáóåòñÿ îïðåäåëèòü,
    èìååòå ëè âû ïðàâà íà äîñòóï ê çàòðåáîâàííîìó ðåñóðñó. Ýòà ÷àñòü ïðîöåññà íàçûâàåòñÿ
    àâòîðèçàöèåé è ïîäðàçóìåâàåò ïðîâåðêó âàøèõ ïðèâèëåãèé íà âûïîëíåíèå òîãî èëè
    èíîãî äåéñòâèÿ.
    210

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïîñêîëüêó ëó÷øèé ïóòü èçó÷èòü ÷òî-ëèáî - óâèäåòü íà ïðèìåðå, äàâàéòå óãëóáèìñÿ â
    âîïðîñû áåçîïàñíîñòè web-ïðèëîæåíèé.

    Ïðèìå÷àíèå:

    security component Symfony äîñòóïåí êàê ñàìîñòîÿòåëüíàÿ PHPáèáëèîòåêà è ìîæåò áûòü èñïîëüçîâàí â ëþáîì PHP-ïðîåêòå.

    2.12.1 Ïðîñòîé ïðèìåð: áàçîâàÿ HTTP àóòåíòèôèêàöèÿ

    Êîìïîíåíò áåçîïàñíîñòè ìîæåò áûòü íàñòðîåí ïðè ïîìîùè êîíôèãóðàöèè ïðèëîæåíèÿ. Íà ñàìîì äåëå, íàèáîëåå ñòàíäàðòíûå ñöåíàðèè áåçîïàñíîñòè ìîæíî íàñòðîèòü
    íåïîñðåäñòâåííî ÷åðåç êîíôèãóðàöèþ. Ñëåäóþùàÿ êîíôèãóðàöèÿ ïîäñêàæåò Symfony,
    ÷òî íóæíî çàùèòèòü ëþáîé URL, ñîîòâåòñòâóþùèé øàáëîíó /admin/*, è çàïðàøèâàòü
    ïîëüçîâàòåëüñêèå äàííûå ïðè ïîìîùè áàçîâîé HTTP-àóòåíòèôèêàöèè (ò.å. ñóðîâûé
    îëäñêóëüíûé áîêñ username/password):
    ˆ

    YAML

    # app/config/security.yml
    security:
    firewalls:
    secured_area:
    pattern:
    ^/
    anonymous: ~
    http_basic:

    2.12.

    Áåçîïàñíîñòü

    211

    Symfony Documentation, Âûïóñê 2.0

    realm: "Secured Demo Area"
    access_control:
    - { path: ^/admin, roles: ROLE_ADMIN }
    providers:
    in_memory:
    users:
    ryan: { password: ryanpass, roles: 'ROLE_USER' }
    admin: { password: kitten, roles: 'ROLE_ADMIN' }
    encoders:
    Symfony\Component\Security\Core\User\User: plaintext

    ˆ

    XML


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:srv="http://symfony.com/schema/dic/services"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/di

















    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'secured_area' => array(
    212

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ),

    ));

    'pattern' => '^/',
    'anonymous' => array(),
    'http_basic' => array(
    'realm' => 'Secured Demo Area',
    ),

    ),
    'access_control' => array(
    array('path' => '^/admin', 'role' => 'ROLE_ADMIN'),
    ),
    'providers' => array(
    'in_memory' => array(
    'users' => array(
    'ryan' => array('password' => 'ryanpass', 'roles' => 'ROLE_USER'),
    'admin' => array('password' => 'kitten', 'roles' => 'ROLE_ADMIN'),
    ),
    ),
    ),
    'encoders' => array(
    'Symfony\Component\Security\Core\User\User' => 'plaintext',
    ),

    Ñîâåò: Ñòàíäàðòíûé äèñòðèáóòèâ Symfony âûäåëÿåò íàñòðîéêó áåçîïàñíîñòè â îò-

    äåëüíûé ôàéë (ïî óìîë÷àíèþ app/config/security.yml). Åñëè âàì íå íóæåí îòäåëüíûé êîíôèãóðàöèîííûé ôàéë äëÿ íàñòðîéêè áåçîïàñíîñòè, âû ìîæåòå ïåðåìåñòèòü åãî êîíòåíò íåïîñðåäñòâåííî â îñíîâíîé êîíôèãóðàöèîííûé ôàéë (ïî óìîë÷àíèþ
    app/config/config.yml).
     ðåçóëüòàòå èñïîëüçîâàíèÿ òàêîé êîíôèãóðàöèè âû ïîëó÷èòå ïîëíîôóíêöèîíàëüíóþ
    ñèñòåìó áåçîïàñíîñòè, êîòîðàÿ âûãëÿäèò ñëåäóþùèì îáðàçîì:
    ˆ Åñòü äâà ïîëüçîâàòåëÿ ñèñòåìû (ryan and admin);
    ˆ Ïîëüçîâàòåëè àóòåíòèôèöèðóþòñÿ ïðè ïîìîùè áàçîâîé HTTP-àóòåíòèôèêàöèè;
    ˆ Ëþáîé URL, ñîîòâåòñòâóþùèé øàáëîíó /admin/*, áóäåò çàùèùåí, è ëèøü ïîëüçîâàòåëü admin ñìîæåò ïîïàñòü òóäà;
    ˆ Ëþáîé URL, ÍÅ ñîîòâåòñòâóþùèé øàáëîíó /admin/*, áóäåò äîñòóïåí ëþáîìó
    ïîëüçîâàòåëþ áåç ââîäà ëîãèíà/ïàðîëÿ.
    Äàâàéòå âçãëÿíåì íà òî, êàê ðàáîòàåò áåçîïàñíîñòü è êàê êàæäàÿ ÷àñòü êîíôèãóðàöèè
    âñòóïàåò â èãðó.

    2.12.

    Áåçîïàñíîñòü

    213

    Symfony Documentation, Âûïóñê 2.0

    2.12.2 Êàê ðàáîòàåò áåçîïàñíîñòü: Àóòåíòèôèêàöèÿ è Àâòîðèçàöèÿ

    Ñèñòåìà áåçîïàñíîñòè Symfony ðàáîòàåò, îïðåäåëÿÿ ëè÷íîñòü ïîëüçîâàòåëÿ (àóòåíòèôèêàöèÿ) è? çàòåì, ïðîâåðÿÿ, èìååò ëè ýòîò ïîëüçîâàòåëü äîñòóï ê êîíêðåòíîìó ðåñóðñó
    èëè URL.
    Áðàíäìàóýðû (Àóòåíòèôèêàöèÿ)

    Êîãäà ïîëüçîâàòåëü âûïîëíÿåò çàïðîñ ê URL, çàùèù¼ííîìó áðàíäìàóýðîì, àêòèâèðóåòñÿ ñèñòåìà áåçîïàñíîñòè. Ðàáîòà áðàíäìàóýðà çàêëþ÷àåòñÿ â îïðåäåëåíèè òîãî, òðåáóåòñÿ ëè àóòåíòèôèêàöèÿ ïîëüçîâàòåëÿ è, åñëè òðåáóåòñÿ, îòïðàâèòü îáðàòíî îòâåò,
    èíèöèèðóþùèé ïðîöåññ àóòåíòèôèêàöèè.
    Áðàíäìàóýð àêòèâèðóåòñÿ, êîãäà URL âõîäÿùåãî çàïðîñà ñîîòâåòñòâóåò ðåãóëÿðíîìó
    âûðàæåíèþ pattern, êîòîðîå áûëî óêàçàíî â êîíôèãóðàöèè. Â äàííîì ïðèìåðå øàáëîí
    pattern (^/) áóäåò ñîîòâåòñòâîâàòü ëþáîìó âõîäÿùåìó çàïðîñó. Òî, ÷òî áðàíäìàóýð
    àêòèâèðóåòñÿ, íå îçíà÷àåò, ÷òî HTTP àóòåíòèôèêàöèÿ (áîêñ ñ ëîãèíîì/ïàðîëåì) áóäåò
    òðåáîâàòüñÿ äëÿ êàæäîãî URL. Ê ïðèìåðó, ïîëüçîâàòåëü ìîæåò ïîëó÷èòü äîñòóï ê /foo
    áåç çàïðîñà àóòåíòèôèêàöèè:

    214

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ýòî ðàáîòàåò, òàê êàê áðàíäìàóýð ïîçâîëÿåò äîñòóï àíîíèìíîìó ïîëüçîâàòåëþ íà îñíîâàíèè ïàðàìåòðà anonymous â íàñòðîéêàõ áåçîïàñíîñòè. Äðóãèìè ñëîâàìè, áðàíäìàóýð
    íå òðåáóåò íåìåäëåííîé àóòåíòèôèêàöèè. È, ïîñêîëüêó äîñòóï ê /foo íå òðåáóåò íèêàêîé îñîáîé ðîëè (role) (ýòî óêàçàíî â ñåêöèè access_control), çàïðîñ áóäåò âûïîëíåí
    áåç àóòåíòèôèêàöèè ïîëüçîâàòåëÿ.
    Åñëè âû óäàëèòå êëþ÷ anonymous, áðàíäìàóýð áóäåò âñåãäà çàïðàøèâàòü ó ïîëüçîâàòåëÿ
    íåìåäëåííîé àóòåíòèôèêàöèè.
    Êîíòðîëü äîñòóïà (Àâòîðèçàöèÿ)

    Åñëè ïîëüçîâàòåëü çàïðàøèâàåò /admin/foo, ïðîöåññ âåä¼ò ñåáÿ èíûì îáðàçîì. Ýòî
    îáóñëîâëåíî òåì, ÷òî â ñåêöèè access_control óêàçàíî, ÷òî ëþáîé URL, ñîîòâåòñòâóþùèé øàáëîíó ^/admin (ò.å. /admin èëè âñ¼ ïðî÷åå, ÷òî ñîîòâåòñòâóåò /admin/*) òðåáóåò
    íàëè÷èÿ ó ïîëüçîâàòåëÿ ðîëè ROLE_ADMIN. Ðîëè ÿâëÿþòñÿ îñíîâîé àâòîðèçàöèè: ïîëüçîâàòåëü ìîæåò ïîëó÷èòü äîñòóï ê /admin/foo ëèøü òîãäà, êîãäà ó íåãî åñòü ðîëü
    ROLE_ADMIN.

    2.12.

    Áåçîïàñíîñòü

    215

    Symfony Documentation, Âûïóñê 2.0

    Êàê è ðàíåå, êîãäà ïîëüçîâàòåëü âûïîëíÿåò çàïðîñ, áðàíäìàóýð íå òðåáóåò èäåíòèôèêàöèè ïîëüçîâàòåëÿ. Òåì íå ìåíåå, êàê òîëüêî êîíòðîëü äîñòóïà îòêàçûâàåò ïîëüçîâàòåëþ â äåéñòâèè (òàê êàê àíîíèìíûé ïîëüçîâàòåëü íå èìååò ðîëè ROLE_ADMIN),
    áðàíäìàóýð âñòóïàåò â èãðó è èíèöèèðóåò ïðîöåññ àóòåíòèôèêàöèè. Ïðîöåññ àóòåíòèôèêàöèè çàâèñèò îò ìåõàíèçìà àóòåíòèôèêàöèè, êîòîðûé âû èñïîëüçóåòå. Íàïðèìåð,
    åñëè âû èñïîëüçóåòå àóòåíòèôèêàöèþ ñ èñïîëüçîâàíèåì ôîðìû ëîãèíà, ïîëüçîâàòåëü
    áóäåò ïåðåíàïðàâëåí íà ñòðàíèöó ëîãèíà. Åñëè èñïîëüçóåòñÿ HTTP-àóòåíòèôèêàöèÿ,
    ïîëüçîâàòåëþ áóäåò íàïðàâëåí îòâåò ñ HTTP ñòàòóñ êîäîì 401 è â áðàóçåðå áóäåò îòîáðàæ¼í áîêñ ñ ïîëÿìè username è password.
    Ïîëüçîâàòåëü òåïåðü èìååò âîçìîæíîñòü îòïðàâèòü ñâîè äàííûå îáðàòíî ïðèëîæåíèþ.
    Åñëè ýòè äàííûå áóäóò âàëèäíûìè, îðèãèíàëüíûé çàïðîñ ïîëüçîâàòåëÿ áóäåò îáðàáîòàí.

    Â ýòîì ïðèìåðå, ïîëüçîâàòåëü ryan óñïåøíî ïðîõîäèò àóòåíòèôèêàöèþ â áðàíäìàóýðå, íî, òàê êàê ryan íå èìååò ðîëè ROLE_ADMIN, îí ïî-ïðåæíåìó íå èìååò äîñòóïà ê
    /admin/foo.  êîíå÷íîì èòîãå, ýòî îçíà÷àåò, ÷òî ïîëüçîâàòåëü óâèäèò íåêîå ñîîáùåíèå,
    î òîì, ÷òî åìó îòêàçàíî â äîñòóïå.

    216

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Êîãäà Symfony çàïðåùàåò äîñòóï ïîëüçîâàòåëþ, åìó îòîáðàæàåòñÿ ñòðàíèöà
    ñ îøèáêîé è âîçâðàùàåòñÿ HTTP ñòàòóñ êîä 403 (Forbidden). Âû ìîæåòå èçìåíèòü
    äèçàéí ñòðàíèöû ñ îøèáêîé 403, ñëåäóÿ ðóêîâîäñòâó èç êíèãè ðåöåïòîâ Error Pages .

    Íàêîíåö, åñëè ïîëüçîâàòåëü admin çàïðàøèâàåò /admin/foo, èìååò ìåñòî ñõîæèé ïðîöåññ, çà òåì èñêëþ÷åíèåì, ÷òî ñèñòåìà êîíòðîëÿ äîñòóïà ðàçðåøèò ïðîõîæäåíèå ýòîãî
    çàïðîñà:

    Ïóòü çàïðîñà, êîãäà ïîëüçîâàòåëü çàïðàøèâàåò çàùèù¼ííûé ðåñóðñ, ïðÿìîëèíååí, íî
    âìåñòå ñ òåì ãèáîê. Êàê âû óâèäèòå ïîçäíåå, àóòåíòèôèêàöèÿ ìîæåò áûòü âûïîëíåíà
    ðàçëè÷íûìè ñïîñîáàìè, âêëþ÷àÿ ôîðìó ëîãèíà, ñåðòèôèêàò X.509 èëè æå ïîñðåäñòâîì
    Twitter. Âíå çàâèñèìîñòè îò ìåòîäà àóòåíòèôèêàöèè, çàïðîñ ïðîõîäèò ñëåäóþùèé ïóòü:
    1. Ïîëüçîâàòåëü çàïðàøèâàåò çàùèù¼ííûé ðåñóðñ;
    2. Ïðèëîæåíèå ïåðåíàïðàâëÿåò ïîëüçîâàòåëÿ íà ôîðìó ëîãèíà (èëè å¼ àíàëîã);
    3. Ïîëüçîâàòåëü îòïðàâëÿåò ñâîè äàííûå (íàïðèìåð username/password);
    4. Áðàíäìàóýð ïðîèçâîäèò àóòåíòèôèêàöèþ ïîëüçîâàòåëÿ;
    5. Àóòåíòèôèöèðîâàííûé ïîëüçîâàòåëü ïîëó÷àåò îðèãèíàëüíûé çàïðîñ.

    2.12.

    Áåçîïàñíîñòü

    217

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Ïðîöåññ àóòåíòèôèêàöèè

    çàâèñèò îò òèïà àóòåíòèôèêàöèè,
    êîòîðûé âû èñïîëüçóåòå. Íàïðèìåð, ïðè èñïîëüçîâàíèè ôîðìû ëîãèíà, ïîëüçîâàòåëü
    îòïðàâëÿåò ñâîè äàííûå ïî URL-àäðåñó, êîòîðûé îáðàáàòûâàåò ôîðìó (íàïðèìåð,
    /login_check) è ïîñëå ýòîãî îí ïåðåíàïðàâëÿåòñÿ íà èçíà÷àëüíî çàïðîøåííûé URL
    (íàïðèìåð, /admin/foo). Íî ïðè èñïîëüçîâàíèè HTTP àóòåíòèôèêàöèè ïîëüçîâàòåëü
    îòïðàâëÿåò ñâîè äàííûå íå óõîäÿ ñ çàïðîøåííîãî URL (íàïðèìåð, /admin/foo) è ïîñëå
    ýòîãî ñòðàíèöà îòïðàâëÿåòñÿ ïîëüçîâàòåëþ áåç ïåðåíàïðàâëåíèé.
    öåëèêîì

    Ýòè îñîáåííîñòè íå äîëæíû âàì ïðè÷èíÿòü ïðîáëåì, íî ëó÷øå î íèõ çíàòü çàðàíåå.

    Ñîâåò:

    Â äàëüíåéøåì âû òàêæå óçíàåòå, êàê ìîæíî çàùèòèòü ëþáîé îáúåêò â
    Symfony2, âêëþ÷àÿ îòäåëüíûå êîíòðîëëåðû, îáúåêòû è äàæå PHP-ìåòîäû.

    2.12.3 Èñïîëüçóåì òðàäèöèîííóþ ôîðìó ëîãèíà

    Äî ñèõ ïîð âû óçíàëè, êàê çàùèòèòü âàøå ïðèëîæåíèå ïðè ïîìîùè áðàíäìàóýðà è,
    çàòåì, îãðàíè÷èòü äîñòóï ê îòäåëüíûì ðàçäåëàì ïðè ïîìîùè ðîëåé. Èñïîëüçóÿ HTTP
    àóòåíòèôèêàöèþ, âû ìîæåòå, íå ïðèëàãàÿ óñèëèé, âîñïîëüçîâàòüñÿ íàòèâíûì îêîøêîì
    äëÿ àóòåíòèôèêàöèè ïðè ïîìîùè ëîãèíà è ïàðîëÿ. Ïîìèìî ýòîãî, Symfony ïîääåðæèâàåò ìíîãî ìåõàíèçìîâ àóòåíòèôèêàöèè èç êîðîáêè. Ïîäðîáíåå ñ íèìè âû ìîæåòå
    îçíàêîìèòüñÿ â ðàçäåëå ñïðàâî÷íîé èíôîðìàöèè Íàñòðîéêà ïàðàìåòðîâ áåçîïàñíîñòè.
    Â ýòîé ñåêöèè âû óëó÷øèòå ïðîöåññ àóòåíòèôèêàöèè, äàâ ïîëüçîâàòåëþ âîçìîæíîñòü
    âîñïîëüçîâàòüñÿ òðàäèöèîííîé ôîðìîé ëîãèíà.
    Âî-ïåðâûõ, àêòèâèðóéòå ôîðìó â áðàíäìàóýðå:
    ˆ

    YAML

    # app/config/security.yml
    security:
    firewalls:
    secured_area:
    pattern:
    ^/
    anonymous: ~
    form_login:
    login_path: /login
    check_path: /login_check

    ˆ

    XML


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:srv="http://symfony.com/schema/dic/services"

    218

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/di








    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'secured_area' => array(
    'pattern' => '^/',
    'anonymous' => array(),
    'form_login' => array(
    'login_path' => '/login',
    'check_path' => '/login_check',
    ),
    ),
    ),
    ));

    Ñîâåò: Åñëè âû íå õîòèòå èçìåíÿòü çíà÷åíèÿ login_path èëè check_path èñïîëüçóåìûå ïî óìîë÷àíèþ, âû ìîæåòå óïðîñòèòü êîíôèãóðàöèþ:
    ˆ

    YAML

    form_login: ~

    ˆ

    XML



    ˆ

    PHP

    'form_login' => array(),

    Òåïåðü, êîãäà ñèñòåìà áåçîïàñíîñòè èíèöèèðóåò ïðîöåññ àóòåíòèôèêàöèè, îíà ïåðåíàïðàâëÿåò ïîëüçîâàòåëÿ íà ôîðìó ëîãèíà (/login ïî óìîë÷àíèþ). Êàê ýòà ôîðìà
    áóäåò âûãëÿäåòü - ýòî âàøà çàáîòà. Ñíà÷àëà ñîçäàéòå äâà ìàðøðóòà: îäèí äëÿ îòîáðàæåíèÿ ôîðìû (ò.å. /login) äðóãîé áóäåò îáðàáàòûâàòü îòïðàâêó ôîðìû ëîãèíà (ò.å.
    /login_check):
    2.12.

    Áåçîïàñíîñòü

    219

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    YAML

    # app/config/routing.yml
    login:
    pattern: /login
    defaults: { _controller: AcmeSecurityBundle:Security:login }
    login_check:
    pattern: /login_check

    ˆ

    XML




    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

    AcmeSecurityBundle:Security:login




    ˆ

    PHP

    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    $collection = new RouteCollection();
    $collection->add('login', new Route('/login', array(
    '_controller' => 'AcmeDemoBundle:Security:login',
    )));
    $collection->add('login_check', new Route('/login_check', array()));
    return $collection;

    Ïðèìå÷àíèå: Âàì íå òðåáóåòñÿ ðåàëèçîâûâàòü êîíòðîëëåð äëÿ URL /login_check,
    òàê êàê áðàíäìàóýð áóäåò àâòîìàòè÷åñêè ïåðåõâàòûâàòü è îáðàáàòûâàòü ôîðìû, îòïðàâëåííûå íà ýòîò URL. Íå îáÿçàòåëüíî, íî ïîëåçíî, áóäåò ñîçäàíèå ìàðøðóòà, êîòîðûé âû áóäåòå èñïîëüçîâàòü äëÿ ãåíåðàöèè URL îòïðàâêè ôîðìû â øàáëîíå ëîãèíà
    íèæå.
    Îáðàòèòå âíèìàíèå, ÷òî íàèìåíîâàíèå ìàðøðóòà login íå îáÿçàòåëüíî. Äåéñòâèòåëü220

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    íî æå âàæíûì ÿâëÿåòñÿ URL ýòîãî ìàðøðóòà (/login), ñîîòâåòñòâóþùèé çíà÷åíèþ
    ïàðàìåòðà login_path, òàê êàê íà íåãî ñèñòåìà áåçîïàñíîñòè áóäåò ïåðåíàïðàâëÿòü
    ïîëüçîâàòåëÿ, êîòîðîìó íóæíî çàëîãèíèòüñÿ.
    Çàòåì, ñîçäàéòå êîíòðîëëåð, êîòîðûé áóäåò îòîáðàæàòü ôîðìó ëîãèíà:
    // src/Acme/SecurityBundle/Controller/Main;
    namespace Acme\SecurityBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\Security\Core\SecurityContext;
    class SecurityController extends Controller
    {
    public function loginAction()
    {
    $request = $this->getRequest();
    $session = $request->getSession();
    // ïîëó÷èòü îøèáêè ëîãèíà, åñëè òàêîâûå èìåþòñÿ
    if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
    $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
    } else {
    $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
    }

    }

    }

    return $this->render('AcmeSecurityBundle:Security:login.html.twig', array(
    // èìÿ, ââåä¼ííîå ïîëüçîâàòåëåì â ïîñëåäíèé ðàç
    'last_username' => $session->get(SecurityContext::LAST_USERNAME),
    'error'
    => $error,
    ));

    Íå äàéòå ýòîìó êîäó çàïóòàòü âàñ. Êàê âû óâèäèòå, êîãäà ïîëüçîâàòåëü îòïðàâëÿåò ôîðìó, ñèñòåìà áåçîïàñíîñòè àâòîìàòè÷åñêè îáðàáàòûâàåò å¼. Åñëè þçåð îòïðàâèë íåâåðíûå
    èìÿ è ïàðîëü, ýòîò êîíòðîëëåð ïîëó÷àåò îøèáêè îò ñèñòåìû áåçîïàñíîñòè, ÷òîáû âû
    ìîãëè èõ îòîáðàçèòü ïîëüçîâàòåëþ.
    Äðóãèìè ñëîâàìè, âàøà ðàáîòà çàêëþ÷àåòñÿ â îòîáðàæåíèè ôîðìû ëîãèíà è îøèáîê,
    êîòîðûå ìîãóò âîçíèêíóòü, â òî âðåìÿ êàê ñèñòåìà áåçîïàñíîñòè áåð¼ò íà ñåáÿ çàáîòó
    ïî ïðîâåðêå ââåä¼ííûõ èìåíè è ïàðîëÿ è àóòåíòèôèêàöèè ïîëüçîâàòåëÿ.
    Íàêîíåö, ñîçäàäèì øàáëîí ôîðìû:
    ˆ

    2.12.

    Twig

    Áåçîïàñíîñòü

    221

    Symfony Documentation, Âûïóñê 2.0

    {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
    {% if error %}
    {{ error.message }}

    {% endif %}





    {#
    #}

    Åñëè âû õîòèòå êîíòðîëèðîâàòü URL, íà êîòîðûé ïåðåíàïðàâèòü ïîëüçîâàòåëÿ:





    ˆ

    PHP



    getMessage() ?>








    -->



    Ñîâåò:

    Ïåðåìåííàÿ error, ïåðåäàâàåìàÿ â øàáëîí, ýòî ýêçåìïëÿð êëàññà
    Symfony\Component\Security\Core\Exception\AuthenticationException. Ýòîò îáúåêò ìîæåò ñîäåðæàòü äîïîëíèòåëüíóþ èíôîðìàöèþ - äàæå ñåêðåòíóþ - îá îøèáêå
    àóòåíòèôèêàöèè, òàê ÷òî èñïîëüçóéòå åãî ñ óìîì!

    222

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ôîðìà èìååò íåìíîãî òðåáîâàíèé. Âî-ïåðâûõ, îòïðàâëÿÿ ôîðìó íà /login_check
    (ìàðøðóò login_check), ñèñòåìà áåçîïàñíîñòè ïåðåõâàòèò îòïðàâëåííóþ ôîðìó è îáðàáîòàåò å¼ àâòîìàòè÷åñêè. Âî âòîðûõ, ñèñòåìà áåçîïàñíîñòè îæèäàåò, ÷òî îòïðàâëåííûå
    ïîëÿ áóäóò íàçûâàòüñÿ _username è _password (ýòè íàèìåíîâàíèÿ òàêæå ìîæíî íàñòðîèòü ).
    Âîò è âñ¼! Êîãäà âû îòïðàâëÿåòå ôîðìó, ñèñòåìà áåçîïàñíîñòè àâòîìàòè÷åñêè ïðîâåðèò
    äàííûå ïîëüçîâàòåëÿ è, ëèáî âûïîëíèòü åãî àóòåíòèôèêàöèþ, ëèáî îòïðàâèòü ïîëüçîâàòåëÿ îáðàòíî íà ôîðìó ëîãèíà, ãäå îí óâèäèò âîçíèêøèå îøèáêè.
    Äàâàéòå åù¼ ðàç âçãëÿíåì íà ïðîöåññ öåëèêîì:
    1. Ïîëüçîâàòåëü ïûòàåòñÿ ïîëó÷èòü äîñòóï ê çàùèù¼ííîìó ðåñóðñó;
    2. Áðàíäìàóýð èíèöèèðóåò ïðîöåññ àóòåíòèôèêàöèè, ïåðåíàïðàâëÿÿ ïîëüçîâàòåëÿ
    íà ôîðìó ëîãèíà (/login);
    3. Ñòðàíèöà /login îòîáðàæàåò ôîðìó ëîãèíà ïðè ïîìîùè ìàðøðóòà è êîíòðîëëåðà,
    ñîçäàííûõ â ýòîì ïðèìåðå;
    4. Ïîëüçîâàòåëü îòïðàâëÿåò ôîðìó ëîãèíà íà URL /login_check;
    5. Ñèñòåìà áåçîïàñíîñòè ïåðåõâàòûâàåò çàïðîñ, ïðîâåðÿåò äàííûå, îòïðàâëåííûå
    ïîëüçîâàòåëåì, àóòåíòèôèöèðóåò ïîëüçîâàòåëÿ, åñëè äàííûå âåðíû èëè æå âîçâðàùàåò ïîëüçîâàòåëþ ñòðàíèöó ëîãèíà, åñëè äàííûå íå âåðíû.
    Ïî óìîë÷àíèþ, åñëè äàííûå ïîëüçîâàòåëÿ âåðíû, ïîëüçîâàòåëü áóäåò ïåðåíàïðàâëåí
    íà òó æå ñòðàíèöó, êîòîðóþ è çàïðàøèâàë (íàïðèìåð, /admin/foo). Åñëè ïîëüçîâàòåëü ñðàçó îòêðûë ñòðàíèöó ëîãèíà, òî îí áóäåò ïåðåíàïðàâëåí íà ãëàâíóþ ñòðàíèöó
    (homepage). Ýòî ïîâåäåíèå ìîæíî íàñòðîèòü, ê ïðèìåðó ðàçðåøèòü ïåðåíàïðàâëåíèå
    ïîëüçîâàòåëÿ íà ôèêñèðîâàííûé URL.
    Äîïîëíèòåëüíóþ èíôîðìàöèþ î òîì, êàê íàñòðàèâàåòñÿ ôîðìà ëîãèíà, ñìîòðèòå ñòàòüþ â êíèãå ðåöåïòîâ /cookbook/security/form_login.

    2.12.

    Áåçîïàñíîñòü

    223

    Symfony Documentation, Âûïóñê 2.0

    Òèïè÷íûå îøèáêè
    Ïðè íàñòðîéêå âàøåé ôîðìû ëîãèíà, èçáåãàéòå ñëåäóþùèõ òèïè÷íûõ îøèáîê.

    1. Ñîçäàâàéòå êîððåêòíûå ìàðøðóòû

    Âî-ïåðâûõ, óäîñòîâåðüòåñü, ÷òî âû êîððåêòíî îïðåäåëèëè ìàðøðóòû /login è
    /login_check è ÷òî îíè ñîîòâåòñòâóþò êîíôèãóðàöèîííûì ïàðàìåòðàì login_path
    è check_path. Îøèáêè çäåñü áóäóò âûçûâàòü ïåðåíàïðàâëåíèå íà ñòðàíèöó 404
    âìåñòî ñòðàíèöû ëîãèíà èëè æå îòïðàâëåííàÿ ôîðìà íå áóäåò îáðàáàòûâàòüñÿ (âû
    áóäåòå âèäåòü ôîðìó ëîãèíà ñíîâà è ñíîâà).

    2. Óäîñòîâåðüòåñü, ÷òî ñòðàíèöà ëîãèíà ÍÅ çàùèùåíà

    Òàêæå, óäîñòîâåðüòåñü, ÷òî ñòðàíèöà ëîãèíà ÍÅ òðåáóåò íèêàêèõ ðîëåé äëÿ äîñòóïà ê íåé. Íàïðèìåð, ñëåäóþùàÿ êîíôèãóðàöèÿ - êîòîðàÿ òðåáóåò ðîëè ROLE_ADMIN
    äëÿ âñåõ URL (âêëþ÷àÿ URL /login), áóäåò âûçûâàòü çàöèêëèâàíèå ïåðåíàïðàâëåíèé:
    ˆ YAML
    access_control:
    - { path: ^/, roles: ROLE_ADMIN }

    ˆ

    XML





    ˆ

    PHP

    'access_control' => array(
    array('path' => '^/', 'role' => 'ROLE_ADMIN'),
    ),

    Óäàëåíèå êîíòðîëÿ äîñòóïà äëÿ URL /login èñïðàâëÿåò ïðîáëåìó:
    ˆ YAML
    access_control:
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, roles: ROLE_ADMIN }

    ˆ

    XML






    ˆ

    PHP

    'access_control' => array(
    array('path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'),
    array('path' => '^/', 'role' => 'ROLE_ADMIN'),
    ),

    Òàêæå, åñëè âàø áðàíäìàóýð íå ðàçðåøàåò äîñòóï àíîíèìíûì ïîëüçîâàòåëÿì, âàì
    íåîáõîäèìî ñîçäàòü îñîáûé áðàíäìàóýð, êîòîðûé ïîçâîëÿåò àíîíèìíîìó ïîëüçîâàòåëþ ïîëó÷èòü äîñòóï ê ñòðàíèöå ëîãèíà:
    224
    Ãëàâà 2. Êíèãà
    ˆ YAML
    firewalls:

    Symfony Documentation, Âûïóñê 2.0

    2.12.4 Àâòîðèçàöèÿ

    Ïåðâûì øàãîì â îáåñïå÷åíèè áåçîïàñíîñòè âñåãäà ÿâëÿåòñÿ àóòåíòèôèêàöèÿ: ïðîöåññ
    èäåíòèôèêàöèè ïîëüçîâàòåëÿ.  Symfony àóòåíòèôèêàöèþ ìîæíî âûïîëíÿòü ðàçëè÷íûìè ñïîñîáàìè, íà÷èíàÿ ñ áàçîâîé HTTP àóòåíòèôèêàöèè è ôîðìû ëîãèíà è çàêàí÷èâàÿ Facebook.
    Ïîñëå òîãî êàê ïîëüçîâàòåëü àóòåíòèôèöèðîâàí, íà÷èíàåòñÿ ïðîöåññ àâòîðèçàöèè. Àâòîðèçàöèÿ ÿâëÿåòñÿ ñòàíäàðòíûì ïóò¼ì îïðåäåëåíèÿ èìååò ëè ïîëüçîâàòåëü ïðàâî äîñòóïà ê êàêîìó-ëèáî ðåñóðñó (URL, îáúåêò ìîäåëè, âûçîâ ìåòîäà...).  îñíîâå ýòîãî
    ïðîöåññà ëåæèò ïðèñâîåíèå íåêîòîðûõ ðîëåé êàæäîìó ïîëüçîâàòåëþ è ïîñëå ýòîãî äëÿ
    ðàçëè÷íûõ ðåñóðñîâ ìîæíî òðåáîâàòü íàëè÷èÿ ðàçëè÷íûõ ðîëåé.
    Àâòîðèçàöèÿ èìååò äâå ðàçëè÷íûå ãðàíè:
    1. Ïîëüçîâàòåëþ íàçíà÷åí íåêîòîðûé íàáîð ðîëåé;
    2. Ðåñóðñ òðåáóåò íàëè÷èÿ íåêîòîðûõ ðîëåé äëÿ ïîëó÷åíèÿ äîñòóïà ê íåìó.
     ýòîé ñåêöèè âû óçíàåòå î òîì, êàê çàùèòèòü ðàçëè÷íûå ðåñóðñû (íàïðèìåð, URL, âûçîâ ìåòîäà è ò.ä.) ïðè ïîìîùè ðàçëè÷íûõ ðîëåé. Çàòåì, âû óçíàåòå î òîì, êàê ñîçäàþòñÿ
    ðîëè è êàê èõ ìîæíî ïðèñâîèòü ïîëüçîâàòåëþ.
    Çàùèùàåì URL ïî øàáëîíó

    Íàèáîëåå ïðîñòîé è ïîíÿòíûé ñïîñîá çàùèòû âàøåãî ïðèëîæåíèÿ - çàùèòà íåêîòîðîãî íàáîðà URL ïî øàáëîíó. Âû óæå âèäåëè ðàíåå, â ïåðâîì ïðèìåðå ýòîé ãëàâû,
    ãäå âñå URL, ÷òî ñîîòâåòñòâîâàëè ðåãóëÿðíîìó âûðàæåíèþ ^/admin, òðåáîâàëè ðîëè
    ROLE_ADMIN.
    Âû ìîæåòå îïðåäåëèòü ñòîëüêî URL, ñêîëüêî âàì íóæíî - êàæäûé ïðè ïîìîùè øàáëîíà
    äëÿ ðåãóëÿðíîãî âûðàæåíèÿ:
    ˆ

    YAML

    # app/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
    - { path: ^/admin, roles: ROLE_ADMIN }

    ˆ

    XML






    2.12.

    Áåçîïàñíîñòü

    225

    Symfony Documentation, Âûïóñê 2.0




    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    // ...
    'access_control' => array(
    array('path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'),
    array('path' => '^/admin', 'role' => 'ROLE_ADMIN'),
    ),
    ));

    Ñîâåò: Äîáàâëåíèå â íà÷àëî ïóòè ñèìâîëà ^ ãàðàíòèðóåò, ÷òî ýòîìó øàáëîíó áóäóò
    ñîîòâåòñòâîâàòü ëèøü URL, êîòîðûå íà÷èíàþòñÿ c íåãî. Íàïðèìåð, ïóòü /admin (áåç
    ^ â íà÷àëå) áóäåò ñîîòâåòñòâîâàòü êàê URL /admin/foo, òàê è URL /foo/admin.

    Äëÿ êàæäîãî âõîäÿùåãî çàïðîñà, Symfony2 ïûòàåòñÿ íàéòè ñîîòâåòñòâóþùåå ïðàâèëî êîíòðîëÿ äîñòóïà (èñïîëüçóåòñÿ ïåðâîå íàéäåííîå ïðàâèëî). Åñëè ïîëüçîâàòåëü
    åù¼ íå ïðîø¼ë àóòåíòèôèêàöèþ, èíèöèèðóåòñÿ ïðîöåññ àóòåíòèôèêàöèè (ò.å. ïîëüçîâàòåëþ ïðåäîñòàâëÿåòñÿ âîçìîæíîñòü çàëîãèíèòüñÿ â ñèñòåìó). Åñëè æå ïîëüçîâàòåëü óæå ïðîø¼ë àóòåíòèôèêàöèþ, íî íå èìååò òðåáóåìîé ðîëè, áóäåò áðîøåíî èñêëþ÷åíèå Symfony\Component\Security\Core\Exception\AccessDeniedException, êîòîðîå
    âû ìîæåòå îáðàáîòàòü è ïîêàçàòü ïîëüçîâàòåëþ êðàñèâóþ ñòðàíè÷êó access denied.
    Ïîäðîáíåå ÷èòàéòå â êíèãå ðåöåïòîâ: Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê
    Òàê êàê Symfony èñïîëüçóåò ïåðâîå íàéäåííîå ïðàâèëî, URL âèäà /admin/users/new
    áóäåò ñîîòâåòñòâîâàòü ïåðâîìó ïðàâèëó è òðåáîâàòü íàëè÷èÿ ðîëè ROLE_SUPER_ADMIN.
    Ëþáîé URL âèäà /admin/blog áóäåò ñîîòâåòñòâîâàòü âòîðîìó ïðàâèëó è òðåáîâàòü íàëè÷èÿ ðîëè ROLE_ADMIN.
    Çàùèòà ïî IP

     æèçíè ìîãóò âîçíèêàòü ðàçëè÷íûå ñèòóàöèè, â êîòîðûõ âàì áóäåò íåîáõîäèìî îãðàíè÷èòü äîñòóï äëÿ íåêîåãî ìàðøðóòà ïî IP. Ýòî îñîáåííî âàæíî â ñëó÷àå èñïîëüçîâàíèÿ Edge Side Includes (ESI), êîòîðûå, íàïðèìåð, èñïîëüçóþò ìàðøðóò ïîä íàçâàíèåì _internal. Êîãäà èñïîëüçóþòñÿ ESI, ìàðøðóò _internal íåîáõîäèì êýøèðóþùåìó
    øëþçó äëÿ ïîäêëþ÷åíèÿ ðàçëè÷íûõ îïöèé êýøèðîâàíèÿ ñóáñåêöèé âíóòðè óêàçàííîé
    ñòðàíèöû. Ýòîò ìàðøðóò ïî óìîë÷àíèþ èñïîëüçóåò ïðåôèêñ ^/_internal â Symfony
    Standard Edition (ïðåäïîëàãàåòñÿ òàêæå ÷òî âû ðàñêîììåíòèðîâàëè ýòè ñòðîêè â ôàéëå ìàðøðóòîâ).
    Íèæå ïðèâîäèòñÿ ïðèìåð òîãî, êàê âû ìîæåòå çàùèòèòü ýòîò ìàðøðóò îò äîñòóïà èçâíå:
    226

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    YAML

    # app/config/security.yml
    security:
    # ...
    access_control:
    - { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }

    ˆ

    XML





    ˆ

    PHP

    'access_control' => array(
    array('path' => '^/_internal', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0
    ),

    Èñïîëüçîâàíèå çàùèù¼ííîãî êàíàëà

    Êàê è çàùèòà íà îñíîâàíèè IP, òðåáîâàíèå èñïîëüçîâàíèÿ SSL äîáàâëÿåòñÿ î÷åíü ïðîñòî:
    ˆ

    YAML

    # app/config/security.yml
    security:
    # ...
    access_control:
    - { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: h

    ˆ

    XML


    */
    public function helloAction($name)
    {
    // ...
    }

    Äîïîëíèòåëüíóþ èíôîðìàöèþ îá ýòîì ïàêåòå âû ìîæåòå ïîëó÷èòü èç äîêóìåíòàöèè
    JMSSecurityExtraBundle. Åñëè âû èñïîëüçóåòå äèñòðèáóòèâ Symfony Standard Edition,
    ýòîò ïàêåò óæå äîñòóïåí âàì ïî óìîë÷àíèþ.  ïðîòèâíîì ñëó÷àå âàì íåîáõîäèìî çàãðóçèòü è óñòàíîâèòü åãî.
    Çàùèòà ïðî÷èõ ñåðâèñîâ

    Ôàêòè÷åñêè, âñ¼ ÷òî óãîäíî â Symfony ìîæåò áûòü çàùèùåíî ïðè ïîìîùè ñòðàòåãèè,
    îïèñàííîé â ïðåäûäóùåé ñåêöèè. Íàïðèìåð, ïðåäïîëîæèì ó âàñ åñòü ñåðâèñ (ò.å. PHP
    êëàññ), êîòîðûé îòñûëàåò email-ñîîáùåíèÿ îò îäíîãî ïîëüçîâàòåëÿ äðóãîìó. Âû ìîæåòå îãðàíè÷èòü èñïîëüçîâàíèå ýòîãî êëàññà - íåâàæíî ãäå îí áóäåò èñïîëüçîâàí - äëÿ
    ïîëüçîâàòåëåé ñ îïðåäåë¼ííîé ðîëüþ.
    Ïîäðîáíåå î òîì êàê âû ìîæåòå èñïîëüçîâàòü êîìïîíåíò áåçîïàñíîñòè äëÿ çàùèòû
    228

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ðàçëè÷íûõ ñåðâèñîâ è ìåòîäîâ â âàøåì ïðèëîæåíèè, ñìîòðèòå ñòàòüþ â êíèãå ðåöåïòîâ:
    /cookbook/security/securing_services.
    Ñïèñêè êîíòðîëÿ äîñòóïà (ACL): Çàùèòà îòäåëüíûõ îáúåêòîâ áàçû äàííûõ

    Ïðåäñòàâüòå, ÷òî âû ïðîåêòèðóåòå áëîã, ãäå ïîëüçîâàòåëè ìîãóò ñîçäàâàòü êîììåíòàðèè ê âàøèì ïîñòàì. Òåïåðü âû õîòèòå, ÷òîáû ïîëüçîâàòåëü èìåë âîçìîæíîñòü ðåäàêòèðîâàòü åãî ñîáñòâåííûé êîììåíòàðèé, íî íå ìîã ðåäàêòèðîâàòü êîììåíòàðèè äðóãèõ
    ïîëüçîâàòåëåé. Òàêæå, â êà÷åñòâå àäìèíèñòðàòîðà, âû õîòèòå èìåòü âîçìîæíîñòü ðåäàêòèðîâàòü êîììåíòàðèè âñåõ ïîëüçîâàòåëåé.
    Êîìïîíåíò áåçîïàñíîñòè ñîäåðæèò îïöèîíàëüíóþ ñèñòåìó ñïèñêîâ êîíòðîëÿ äîñòóïà
    (ACL), êîòîðóþ âû ìîæåòå èñïîëüçîâàòü ïðè íåîáõîäèìîñòè êîíòðîëÿ äîñòóïà ê îòäåëüíûì ýêçåìïëÿðàì îáúåêòîâ â âàøåé ñèñòåìå. Áåç èñïîëüçîâàíèÿ ACL, âû ìîæåòå
    çàùèòèòü ñâîþ ñèñòåìó òàêèì îáðàçîì, ÷òî ëèøü íåêîòîðûå ïîëüçîâàòåëè ñìîãóò èìåòü
    âîçìîæíîñòü ðåäàêòèðîâàíèÿ êîììåíòàðèåâ. Íî ñ ïîìîùüþ ACL, âû ìîæåòå îãðàíè÷èòü ëè ðàçðåøèòü äîñòóï ê êàæäîìó êîíêðåòíîìó êîììåíòàðèþ.
    Ïîäðîáíåå ÷èòàéòå â êíèãå ðåöåïòîâ: /cookbook/security/acl.
    2.12.5 Ïîëüçîâàòåëè

     ïðåäûäóùåé ñåêöèè âû óçíàëè êàê ìîæíî çàùèòèòü ðàçëè÷íûå ðåñóðñû, òðåáóÿ äëÿ
    íèõ íàëè÷èÿ îäíîé èëè íåñêîëüêèõ ðîëåé.  ýòîé ñåêöèè âû óçíàåòå î äðóãîé ãðàíè
    àâòîðèçàöèè: ïîëüçîâàòåëÿõ.

    Ïðîâàéäåðû Ïîëüçîâàòåëåé )

    Îòêóäà áåðóòñÿ ïîëüçîâàòåëè? (

    Âî âðåìÿ àóòåíòèôèêàöèè, ïîëüçîâàòåëü îòïðàâëÿåò íåêîòîðûå äàííûå (êàê ïðàâèëî
    èìÿ è ïàðîëü). Ðàáîòà ñèñòåìû àóòåíòèôèêàöèè çàêëþ÷àåòñÿ â òîì, ÷òîáû ïðîâåðèòü
    ýòè äàííûå íà íåêîòîðîì íàáîðå ïîëüçîâàòåëåé. Îòêóäà æå áåðóòñÿ ýòè ïîëüçîâàòåëè?
    Â Symfony2 ïîëüçîâàòåëè ìîãóò ïîÿâëÿòüñÿ îòîâñþäó - èç ôàéëà êîíôèãóðàöèè, áàçû
    äàííûõ, âåá ñåðâèñà èëè îòêóäà âàøåé äóøå óãîäíî áóäåò. Âñ¼, ÷òî ïðåäîñòàâëÿåò îäíîãî
    èëè áîëåå ïîëüçîâàòåëåé ñèñòåìå àóòåíòèôèêàöèè íàçûâàåòñÿ ïðîâàéäåðîì ïîëüçîâàòåëÿ (user provider). Symfony2 ïîñòàâëÿåòñÿ ñ äâóìÿ, íàèáîëåå ïðîñòûìè ïðîâàéäåðàìè:
    îäèí èç íèõ çàãðóæàåò ïîëüçîâàòåëåé èç êîíôèãóðàöèîííîãî ôàéëà, äðóãîé çàãðóæàåò
    ïîëüçîâàòåëåé èç òàáëèöû â áàçå äàííûõ.

    Îïðåäåëåíèå ïîëüçîâàòåëåé â ôàéëå êîíôèãóðàöèè
    Íàèáîëåå ïðîñòîé ñïîñîá ñîçäàòü ïîëüçîâàòåëåé - îïðåäåëèòü èõ ïðÿìî â ôàéëå êîíôèãóðàöèè. Ôàêòè÷åñêè âû óæå âèäåëè ýòîò ñïîñîá ðàíåå â îäíîì èç ïðèìåðîâ ýòîé
    2.12.

    Áåçîïàñíîñòü

    229

    Symfony Documentation, Âûïóñê 2.0

    ãëàâû.
    ˆ

    YAML

    # app/config/config.yml
    security:
    # ...
    providers:
    default_provider:
    users:
    ryan: { password: ryanpass, roles: 'ROLE_USER' }
    admin: { password: kitten, roles: 'ROLE_ADMIN' }

    ˆ

    XML










    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    // ...
    'providers' => array(
    'default_provider' => array(
    'users' => array(
    'ryan' => array('password' => 'ryanpass', 'roles' => 'ROLE_USER'),
    'admin' => array('password' => 'kitten', 'roles' => 'ROLE_ADMIN'),
    ),
    ),
    ),
    ));

    Òàêîé ïðîâàéäåð íàçûâàåòñÿ ïðîâàéäåðîì â ïàìÿòè (in-memory), òàê êàê ïîëüçîâàòåëè íå ñîõðàíåíû ãäå-ëèáî â áàçå äàííûõ. Â èòîãå ïðåäîñòàâëÿåòñÿ îáúåêò êëàññà
    Symfony\Component\Security\Core\User\User.

    Ñîâåò: Ëþáîé ïðîâàéäåð ïîëüçîâàòåëåé ìîæåò çàãðóæàòü ïîëüçîâàòåëåé íåïîñðåä-

    ñòâåííî èç êîíôèãóðàöèè, åñëè äëÿ íåãî óêàçàí ïàðàìåòð users è îïðåäåëåíû ïîëüçîâàòåëè.

    230

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Îñòîðîæíî: Åñëè èìÿ âàøåãî ïîëüçîâàòåëÿ ïîëíîñòüþ öèôðîâîå (íàïðèìåð, 77)
    èëè ñîäåðæèò òèðå (íàïðèìåð, user-name), âû äîëæíû èñïîëüçîâàòü àëüòåðíàòèâíûé
    ñèíòàêñèñ ïðè ñîçäàíèè ïîëüçîâàòåëåé â YAML ôàéëå:

    users:
    - { name: 77, password: pass, roles: 'ROLE_USER' }
    - { name: user-name, password: pass, roles: 'ROLE_USER' }

    Äëÿ íåáîëüøèõ ñàéòîâ ýòîò ìåòîä áûñòð è ïðîñò â íàñòðîéêå. Äëÿ áîëåå ñëîæíûõ
    ñèñòåì âû âåðîÿòíî çàõîòèòå çàãðóæàòü ïîëüçîâàòåëåé èç áàçû äàííûõ.

    Çàãðóçêà ïîëüçîâàòåëåé èç áàçû äàííûõ
    Åñëè âû õîòèòå çàãðóæàòü ïîëüçîâàòåëåé èç áàçû äàííûõ ïðè ïîìîùè Doctrine ORM,
    âû ìîæåòå ýòîãî ëåãêî äîñòè÷ü, ñîçäàâ êëàññ User è íàñòðîèâ ïðîâàéäåð entity.
    Ïðè òàêîì ïîäõîäå âû ñíà÷àëà ñîçäà¼òå ñâîé ñîáñòâåííûé êëàññ User, êîòîðûé áóäåò
    ñîõðàíÿòüñÿ â áàçå äàííûõ:
    // src/Acme/UserBundle/Entity/User.php
    namespace Acme\UserBundle\Entity;
    use Symfony\Component\Security\Core\User\UserInterface;
    use Doctrine\ORM\Mapping as ORM;
    /**
    * @ORM\Entity
    */
    class User implements UserInterface
    {
    /**
    * @ORM\Column(type="string", length="255")
    */
    protected $username;
    }

    // ...

    ×òî æå êà÷àåòñÿ
    ê âàøåìó êëàññó

    åäèíñòâåííûì å¼ òðåáîâàíèåì
    èìïëåìåíòàöèÿ èì èíòåðôåéñà
    Symfony\Component\Security\Core\User\UserInterface. Ýòî îçíà÷àåò, ÷òî âàøà
    êîíöåïöèÿ ïîëüçîâàòåëÿ ìîæåò áûòü êàêîé óãîäíî, êîëü ñêîðî êëàññ èìïëåìåíòèðóåò
    ýòîò èíòåðôåéñ.

    2.12.

    Áåçîïàñíîñòü

    ñèñòåìû áåçîïàñíîñòè,
    ïîëüçîâàòåëÿ ÿâëÿåòñÿ

    231

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Îáúåêò ïîëüçîâàòåëÿ áóäåò ñåðèàëèçîâàí è ñîõðàí¼í â ñåññèè âî âðå-

    ìÿ îáðàáîòêè çàïðîñà, ïîýòîìó ðåêîìåíäóåòñÿ òàêæå èìïëåìåíòèðîâàòü èíòåðôåéñ
    Serializable äëÿ âàøåãî ïîëüçîâàòåëÿ. Ýòî îñîáåííî âàæíî, åñëè âàø êëàññ User èìååò
    ðîäèòåëÿ ñ ïðèâàòíûìè ñâîéñòâàìè.
    Äàëåå, íàñòðîèì ïðîâàéäåð entity è óêàæåì äëÿ íåãî êëàññ User:
    ˆ

    YAML

    # app/config/security.yml
    security:
    providers:
    main:
    entity: { class: Acme\UserBundle\Entity\User, property: username }

    ˆ

    XML








    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'providers' => array(
    'main' => array(
    'entity' => array('class' => 'Acme\UserBundle\Entity\User', 'property' => 'user
    ),
    ),
    ));

    Äîáàâèâ ýòîò íîâûé ïðîâàéäåð, ñèñòåìà àóòåíòèôèêàöèè áóäåò ïûòàòüñÿ çàãðóçèòü îáúåêò User èç áàçû äàííûõ, èñïîëüçóÿ åãî ïîëå username.

    Ïðèìå÷àíèå:

    Ýòîò ïðèìåð ïðåäíàçíà÷åí ÷òîáû ïîêàçàòü âàì îñíîâíóþ èäåþ
    ïðîâàéäåðà entity. Ïîëíûé ðàáî÷èé ïðèìåð ïðèâîäèòñÿ â êíèãå ðåöåïòîâ:
    /cookbook/security/entity_provider.
    Áîëüøå èíôîðìàöèè î ñîçäàíèè âàøåãî ñîáñòâåííîãî ïðîâàéäåðà (íàïðèìåð,
    åñëè âàì íóæíî çàãðóæàòü ïîëüçîâàòåëåé èç âåá-ñåðâèñà), ñìîòðèòå ñòàòüþ
    /cookbook/security/custom_provider.

    232

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Øèôðîâàíèå ïàðîëÿ ïîëüçîâàòåëÿ

    Ðàíåå, äëÿ óïðîùåíèÿ, âñå ïðèìåðû õðàíèëè ïàðîëè ïîëüçîâàòåëåé â âèäå òåêñòà (âíå
    çàâèñèìîñòè îò òîãî ãäå ýòè ïîëüçîâàòåëè áûëè îïðåäåëåíû - â ôàéëå íàñòðîåê èëè â
    áàçå äàííûõ). Êîíå÷íî, â íàñòîÿùåì ïðèëîæåíèè âû çàõîòèòå øèôðîâàòü ïàðîëè ïîëüçîâàòåëåé èç ñîîáðàæåíèé áåçîïàñíîñòè. Ýòîãî ëåãêî äîñòè÷ü, ñâÿçàâ âàø êëàññ User
    ñ îäíèì èç íåñêîëüêèõ âñòðîåííûõ ïðîöåäóð øèôðîâàíèÿ. Íàïðèìåð, ïðè õðàíåíèè
    âàøèõ ïîëüçîâàòåëåé â ïàìÿòè, ÷òîáû ñêðûâàòü èõ ïàðîëè ïðè ïîìîùè ôóíêöèè sha1,
    âûïîëíèòå ñëåäóþùèå íàñòðîéêè:
    ˆ

    YAML

    # app/config/config.yml
    security:
    # ...
    providers:
    in_memory:
    users:
    ryan: { password: bb87a29949f3a1ee0559f8a57357487151281386, roles: 'ROLE_U
    admin: { password: 74913f5cd5f61ec0bcfdb775414c2fb3d161b620, roles: 'ROLE_A
    encoders:
    Symfony\Component\Security\Core\User\User:
    algorithm: sha1
    iterations: 1
    encode_as_base64: false

    ˆ

    XML







    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    // ...
    'providers' => array(
    'in_memory' => array(

    2.12.

    Áåçîïàñíîñòü

    233

    Symfony Documentation, Âûïóñê 2.0

    'users' => array(
    'ryan' => array('password' => 'bb87a29949f3a1ee0559f8a57357487151281386', '
    'admin' => array('password' => '74913f5cd5f61ec0bcfdb775414c2fb3d161b620',
    ),

    ));

    ),
    ),
    'encoders' => array(
    'Symfony\Component\Security\Core\User\User' => array(
    'algorithm'
    => 'sha1',
    'iterations'
    => 1,
    'encode_as_base64' => false,
    ),
    ),

    Ïðèñâîèâ ïàðàìåòðó iterations çíà÷åíèå 1 è ïàðàìåòðó encode_as_base64 - false,
    ïàðîëü áóäåò ïðîñòî ïðîãîíÿòüñÿ îäèí ðàç ÷åðåç àëãîðèòì øèôðîâàíèÿ sha1 áåç äîïîëíèòåëüíîãî øèôðîâàíèÿ. Òåïåðü âû ìîæåòå âû÷èñëèòü õýø ïàðîëÿ ïðîãðàìíî
    (hash('sha1', 'ryanpass')) èëè æå ïðè ïîìîùè îíëàéí-èíñòðóìåíòîâ òèïà functionsonline.com.
    Åñëè âû ñîçäà¼òå âàøèõ ïîëüçîâàòåëåé äèíàìè÷åñêè (è õðàíèòå èõ â áàçå äàííûõ),
    âû ìîæåòå èñïîëüçîâàòü áîëåå ñëîæíûå àëãîðèòìû øèôðîâàíèÿ, à çàòåì ïåðåäàâàòü
    îðèãèíàë ïàðîëÿ îáúåêòó-øèôðîâàëüùèêó äëÿ õåøèðîâàíèÿ ïàðîëåé. Íàïðèìåð, ïðåäïîëîæèì ÷òî âàø îáúåêò User - ýòî ýêçåìïëÿð êëàññà Acme\UserBundle\Entity\User
    (êàê â ïðèìåðå âûøå). Ñíà÷àëà íàñòðîéòå øèôðîâàíèå äëÿ ýòîãî êëàññà ïîëüçîâàòåëÿ:
    ˆ

    YAML

    # app/config/config.yml
    security:
    # ...
    encoders:
    Acme\UserBundle\Entity\User: sha512

    ˆ

    XML







    ˆ

    234

    PHP

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // app/config/config.php
    $container->loadFromExtension('security', array(
    // ...

    ));

    'encoders' => array(
    'Acme\UserBundle\Entity\User' => 'sha512',
    ),

     ýòîì ñëó÷àå âû èñïîëüçóåòå áîëåå ñòîéêèé àëãîðèòì sha512. Òàêæå, ïîñêîëüêó âû
    ïðîñòî óêàçàëè àëãîðèòì øèôðîâàíèÿ â âèäå ñòðîêè (sha512), ñèñòåìà áóäåò ïî óìîë÷àíèþ õýøèðîâàòü âàø ïàðîëü 5000 ðàç ïîäðÿä è çàòåì øèôðîâàòü åãî â base64. Äðóãèìè
    ñëîâàìè, ïàðîëü áóäåò ìíîãîêðàòíî çàøèôðîâàí è ïàðîëü íå ñìîæåò áûòü äåêîäèðîâàí
    (ò.å. áóäåò íåâîçìîæíî îïðåäåëèòü îðèãèíàë ïàðîëÿ ïî åãî õýøó).
    Åñëè ó âàñ ïðåäóñìîòðåíà íåêàÿ ðåãèñòðàöèÿ äëÿ ïîëüçîâàòåëåé, âàì ïîòðåáóåòñÿ îïðåäåëèòü õýø ïàðîëÿ, ÷òîáû ïðèñâîèòü åãî ïîëüçîâàòåëþ. Âíå çàâèñèìîñòè îò àëãîðèòìà
    øèôðîâàíèÿ, êîòîðûé âû íàñòðîèëè äëÿ îáúåêòà ïîëüçîâàòåëÿ, â êîíòðîëëåðå ïîëó÷èòü õýø ïàðîëÿ ìîæíî ñëåäóþùèì îáðàçîì:
    // ...
    $factory = $this->get('security.encoder_factory');
    $user = new Acme\UserBundle\Entity\User();
    $encoder = $factory->getEncoder($user);
    $password = $encoder->encodePassword('ryanpass', $user->getSalt());
    $user->setPassword($password);

    Ïîëó÷åíèå îáúåêòà ïîëüçîâàòåëÿ

    Ïîñëå àóòåíòèôèêàöèè, îáúåêò User äëÿ òåêóùåãî þçåðà ìîæíî ïîëó÷èòü ÷åðåç ñåðâèñ
    security.context. Â êîíòðîëëåðå ýòî áóäåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:
    public function indexAction()
    {
    $user = $this->get('security.context')->getToken()->getUser();
    }

    Â êîíòðîëëåðå ìîæíî èñïîëüçâàòü øîðòêàò:
    public function indexAction()
    {
    2.12.

    Áåçîïàñíîñòü

    235

    Symfony Documentation, Âûïóñê 2.0

    $user = $this->getUser();

    }

    Ïðèìå÷àíèå: Àíîíèìíûå ïîëüçîâàòåëè òåõíè÷åñêè ñ÷èòàþòñÿ òàêæå àóòåíòèôèöè-

    ðîâàííûìè, ò.å. ìåòîä isAuthenticated() àíîíèìíîãî ïîëüçîâàòåëÿ áóäåò âîçâðàùàòü
    true. Äëÿ òîãî, ÷òîáû äåéñòâèòåëüíî óáåäèòüñÿ, ÷òî âàø ïîëüçîâàòåëü ïðîø¼ë àóòåíòèôèêàöèþ, íåîáõîäèìî ïðîâåðèòü íàëè÷èå ðîëè IS_AUTHENTICATED_FULLY.

    Èñïîëüçîâàíèå íåñêîëüêèõ ïðîâàéäåðîâ ïîëüçîâàòåëåé

    Ëþáîé ìåõàíèçì àóòåíòèôèêàöèè (HTTP àóòåíòèôèêàöèÿ, ôîðìà ëîãèíà è ò.ï.) èñïîëüçóåò òîëüêî îäèí ïðîâàéäåð è áóäåò ïî óìîë÷àíèþ èñïîëüçîâàòü ïåðâûé óêàçàííûé. Íî ÷òî, åñëè âû õîòèòå óêàçàòü íåñêîëüêî ïîëüçîâàòåëåé ïðè ïîìîùè êîíôèãóðàöèè è îñòàëüíûõ ïîëüçîâàòåëåé ñîõðàíÿòü â áàçó äàííûõ? Ìîæíî ñîçäàòü íîâûé
    chain-ïðîâàéäåð, êîòîðûé ïîçâîëèò äîáèòüñÿ ýòîãî:
    ˆ

    YAML

    # app/config/security.yml
    security:
    providers:
    chain_provider:
    providers: [in_memory, user_db]
    in_memory:
    users:
    foo: { password: test }
    user_db:
    entity: { class: Acme\UserBundle\Entity\User, property: username }

    ˆ

    XML




    in_memory
    user_db









    236

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    'providers' => array(
    'chain_provider' => array(
    'providers' => array('in_memory', 'user_db'),
    ),
    'in_memory' => array(
    'users' => array(
    'foo' => array('password' => 'test'),
    ),
    ),
    'user_db' => array(
    'entity' => array('class' => 'Acme\UserBundle\Entity\User', 'property' => 'user
    ),
    ),
    ));

    Òåïåðü, ëþáîé ìåõàíèçì àóòåíòèôèêàöèè áóäåò èñïîëüçîâàòü chain_provider, òàê êàê
    îí óêàçàí ïåðâûì.  ñâîþ î÷åðåäü, chain_provider áóäåò ïûòàòüñÿ ïîëó÷èòü ïîëüçîâàòåëÿ êàê èç ïðîâàéäåðà in_memory, òàê è èç user_db.

    Ñîâåò: Åñëè âàì íå òðåáóåòñÿ ðàçäåëÿòü ïîëüçîâàòåëåé in_memory îò ïîëüçîâàòåëåé
    user_db, âû ìîæåòå äîñòèãíóòü òîãî æå ýôôåêòà åù¼ áûñòðåå, ñêîìáèíèðîâàâ ýòè äâà
    ðåñóðñà â îäèí ïðîâàéäåð:
    ˆ

    YAML

    # app/config/security.yml
    security:
    providers:
    main_provider:
    users:
    foo: { password: test }
    entity: { class: Acme\UserBundle\Entity\User, property: username }

    ˆ

    XML









    2.12.

    Áåçîïàñíîñòü

    237

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    'providers' => array(
    'main_provider' => array(
    'users' => array(
    'foo' => array('password' => 'test'),
    ),
    'entity' => array('class' => 'Acme\UserBundle\Entity\User', 'property' => 'user
    ),
    ),
    ));

    Âû òàêæå ìîæåòå íàñòðîèòü áðàíäìàóýð èëè èíäèâèäóàëüíûé ìåõàíèçì àóòåíòèôèêàöèè íà èñïîëüçîâàíèå êîíêðåòíîãî ïðîâàéäåðà. Íàïîìèíàåì, åñëè ïðîâàéäåð ÿâíî íå
    óêàçàí, áóäåò èñïîëüçîâàòüñÿ ïåðâûé ïî ñïèñêó:
    ˆ

    YAML

    # app/config/config.yml
    security:
    firewalls:
    secured_area:
    # ...
    provider: user_db
    http_basic:
    realm: "Secured Demo Area"
    provider: in_memory
    form_login: ~

    ˆ

    XML










    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(

    238

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ));

    'firewalls' => array(
    'secured_area' => array(
    // ...
    'provider' => 'user_db',
    'http_basic' => array(
    // ...
    'provider' => 'in_memory',
    ),
    'form_login' => array(),
    ),
    ),

    Â ýòîì ïðèìåðå, åñëè ïîëüçîâàòåëü ïûòàåòñÿ çàëîãèíèòüñÿ ïðè ïîìîùè HTTP àóòåíòèôèêàöèè - áóäåò èñïîëüçîâàòüñÿ ïðîâàéäåð in_memory, íî åñëè ïîëüçîâàòåëü ïîïûòàåòñÿ
    çàëîãèíèòüñÿ ïðè ïîìîùè ôîðìû ëîãèíà, áóäåò èñïîëüçîâàí ïðîâàéäåð user_db (òàê
    êàê ýòîò ïðîâàéäåð ÿâëÿåòñÿ ïðîâàéäåðîì ïî óìîë÷àíèþ äëÿ âñåãî áðàíäìàóýðà).
    Ïîäðîáíóþ èíôîðìàöèþ î ïðîâàéäåðàõ ïîëüçîâàòåëåé è íàñòðîéêàõ áðàíäìàóýðà âû
    ìîæåòå ïðî÷èòàòü â ñïðàâî÷íèêå: /reference/configuration/security.
    2.12.6 Ðîëè

    Ðîëü èìååò êëþ÷åâîå çíà÷åíèå â ïðîöåññå àâòîðèçàöèè. Êàæäûé ïîëüçîâàòåëü ïîëó÷àåò íàáîð ðîëåé è êàæäûé ðåñóðñ òðåáóåò íàëè÷èå îäíîé èëè íåñêîëüêèõ ðîëåé. Åñëè
    ïîëüçîâàòåëü èìååò íåîáõîäèìóþ ðîëü - äîñòóï áóäåò ðàçðåø¼í.  ïðîòèâíîì ñëó÷àå äîñòóï áóäåò çàïðåù¼í.
    Ðîëè, ïî ñóòè, î÷åíü ïðîñòû, ýòî ñòðîêè, êîòîðûå âû ìîæåòå ñîçäàâàòü è èñïîëüçîâàòü ïî ìåðå íàäîáíîñòè (òåì íå ìåíåå, âíóòðè ñèñòåìû ðîëè ýòî âñ¼-òàêè îáúåêòû).
    Íàïðèìåð, åñëè âàì íóæíî îãðàíè÷èòü äîñòóï ê àäìèíêå áëîãà íà âàøåì ñàéòå, âû
    ìîæåòå çàùèòèòü ýòó ñåêöèþ, èñïîëüçóÿ ðîëü ROLE_BLOG_ADMIN. Ýòà ðîëü íå äîëæíà
    áûòü íèãäå îïðåäåëåíà, âû ïðîñòî íà÷èíàåòå å¼ èñïîëüçîâàòü è âñ¼.

    Ïðèìå÷àíèå: Âñå ðîëè â Symfony2 äîëæíû íà÷èíàòüñÿ ñ ïðåôèêñà ROLE_. Åñëè âû

    îïðåäåëÿåòå âàøè ðîëè â îòäåëüíîì êëàññå Role (ïðîäâèíóòûé âàðèàíò), èñïîëüçîâàòü
    ïðåôèêñ ROLE_ íå íóæíî.

    Èåðàðõè÷åñêèå ðîëè

    Âìåñòî òîãî, ÷òîáû ïðèñâàèâàòü ïîëüçîâàòåëþ ìíîãî ðîëåé, âû ìîæåòå îïðåäåëèòü
    ïðàâèëà íàñëåäîâàíèÿ ðîëåé, ñîçäàâ èõ èåðàðõèþ:
    ˆ
    2.12.

    YAML

    Áåçîïàñíîñòü

    239

    Symfony Documentation, Âûïóñê 2.0

    # app/config/security.yml
    security:
    role_hierarchy:
    ROLE_ADMIN:
    ROLE_USER
    ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    ˆ

    XML



    ROLE_USER
    ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH


    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'role_hierarchy' => array(
    'ROLE_ADMIN'
    => 'ROLE_USER',
    'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'),
    ),
    ));

    Â ïðèìåðå âûøå, ïîëüçîâàòåëü ñ ðîëüþ ROLE_ADMIN áóäåò òàêæå èìåòü ðîëü ROLE_USER.
    Ðîëü ROLE_SUPER_ADMIN âêëþ÷àåò â ñåáÿ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH, è
    ROLE_USER (óíàñëåäîâàâ å¼ îò ROLE_ADMIN).
    2.12.7 Âûõîä èç ñèñòåìû

    Êàê ïðàâèëî, âû òàêæå çàõîòèòå äàòü âàøèì ïîëüçîâàòåëÿì âîçìîæíîñòü âûéòè èç
    ñèñòåìû. Ê ñ÷àñòüþ, áðàíäìàóýð ïîçâîëÿåò îáðàáàòûâàòü âûõîä àâòîìàòè÷åñêè, åñëè
    âû àêòèâèðóåòå ïàðàìåòð logout â êîíôèãóðàöèè:
    ˆ

    YAML

    # app/config/config.yml
    security:
    firewalls:
    secured_area:
    # ...
    logout:
    path: /logout
    target: /
    # ...

    240

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    XML










    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'secured_area' => array(
    // ...
    'logout' => array('path' => 'logout', 'target' => '/'),
    ),
    ),
    // ...
    ));

    Áóäó÷è íàñòðîåííîé äëÿ âàøåãî áðàíäìàóýðà, ýòà êîíôèãóðàöèÿ ïðè íàïðàâëåíèè ïîëüçîâàòåëÿ íà /logout (èëè ëþáîé äðóãîé ïóòü, êîòîðûé âû óêàæåòå â ïàðàìåòðå path)
    áóäåò äå-àóòåíòèôèöèðîâàòü åãî. Ýòîò ïîëüçîâàòåëü áóäåò ïåðåíàïðàâëåí íà ãëàâíóþ
    ñòðàíèöó ñàéòà (òàêæå ìîæåò áûòü íàñòðîåíî ïðè ïîìîùè ïàðàìåòðà target). Îáà ýòè
    ïàðàìåòðà - path è target èìåþò ïàðàìåòðû ïî óìîë÷àíèþ, òàêèå æå, êàê óêàçàíû â
    ïðèìåðå âûøå. Äðóãèìè ñëîâàìè, âû ìîæåòå èõ íå óêàçûâàòü, ÷òî óïðîñòèò íàñòðîéêó:
    ˆ

    YAML

    logout: ~

    ˆ

    XML



    ˆ

    PHP

    'logout' => array(),

    Îòìåòèì òàêæå, ÷òî âàì íå íóæíî ñîçäàâàòü êîíòðîëëåð äëÿ URL /logout, òàê êàê
    áðàíäìàóýð ñàì ïîçàáîòèòñÿ îáî âñ¼ì. Âîçìîæíî, âû òàêæå çàõîòèòå ñîçäàòü ìàðøðóò
    è èñïîëüçîâàòü åãî äëÿ ãåíåðàöèè URL:
    ˆ

    2.12.

    YAML

    Áåçîïàñíîñòü

    241

    Symfony Documentation, Âûïóñê 2.0

    # app/config/routing.yml
    logout:
    pattern: /logout

    ˆ

    XML




    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing



    ˆ

    PHP

    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    $collection = new RouteCollection();
    $collection->add('logout', new Route('/logout', array()));
    return $collection;

    Ïîñëå òîãî êàê ïîëüçîâàòåëü âûõîäèò èç ñèñòåìû, îí áóäåò ïåðåíàïðàâëåí ïî ïóòè,
    óêàçàííîìó â ïàðàìåòðå target (íàïðèìåð homepage). Ïîäðîáíåå î êîíôèãóðàöèè logout
    ÷èòàéòå â Ñïðàâî÷íèêå ïî íàñòðîéêå ñèñòåìû áåçîïàñíîñòè.
    2.12.8 Êîíòðîëü äîñòóïà â øàáëîíàõ

    Åñëè âû õîòèòå ïðîâåðèòü, èìååò ëè ïîëüçîâàòåëü íåêîòîðóþ ðîëü âíóòðè øàáëîíà,
    âîñïîëüçóéòåñü âñòðîåííûì õåëïåðîì:
    ˆ

    Twig

    {% if is_granted('ROLE_ADMIN') %}
    Delete
    {% endif %}

    ˆ

    242

    PHP

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    isGranted('ROLE_ADMIN')): ?>
    Delete


    Ïðèìå÷àíèå: Åñëè âû èñïîëüçóåòå ýòó ôóíêöèþ íà ñòðàíèöå, URL êîòîðîé íå îáðà-

    áàòûâàåòñÿ áðàíäìàóýðîì, áóäåò áðîøåíî èñêëþ÷åíèå. Íàïîìíèì åù¼ ðàç, ÷òî â áîëüøèíñòâå ñëó÷àåâ õîðîøåé ïðàêòèêîé ÿâëÿåòñÿ íàëè÷èå ãëàâíîãî áðàíäìàóýðà, êîòîðûé
    êîíòðîëèðóåò âñå URL (êàê áûëî ïîêàçàíî â ýòîé ãëàâå).

    2.12.9 Êîíòðîëü äîñòóïà â êîíòðîëëåðàõ

    Åñëè âû õîòèòå ïðîâåðèòü, èìååò ëè òåêóùèé ïîëüçîâàòåëü òó èëè èíóþ ðîëü â âàøåì
    êîíòðîëëåðå, èñïîëüçóéòå ìåòîä isGranted êîíòåêñòà áåçîïàñíîñòè:
    public function indexAction()
    {
    // show different content to admin users
    if ($this->get('security.context')->isGranted('ADMIN')) {
    // Çàãðóæàåì àäìèí-êîíòåíò
    }
    // Çàãðóæàåì ïðî÷èé êîíòåíò
    }

    Ïðèìå÷àíèå: Áðàíäìàóýð äîëæåí áûòü àêòèâåí, èëè æå áóäåò áðîøåíî èñêëþ÷åíèå
    ïðè âûçîâå ìåòîäà isGranted. Ïîñìîòðèòå òàêæå çàìå÷àíèå äëÿ øàáëîíîâ ÷óòü âûøå.

    2.12.10 Ïîäìåíà ïîëüçîâàòåëÿ

    Èíîãäà íåîáõîäèìî èìåòü âîçìîæíîñòü ïåðåêëþ÷åíèÿ ñ îäíîãî ïîëüçîâàòåëÿ íà äðóãîãî
    áåç âûïîëíåíèÿ âûõîäà/âõîäà (íàïðèìåð, ïðè îòëàäêå èëè ïðè ïîïûòêå âîñïðîèçâåñòè
    áàã, êîòîðûé ïîëüçîâàòåëü âèäèò, à âû íåò). Ýòî ìîæíî âûïîëíèòü ïðè ïîìîùè ëèñòåíåðà switch_user â áðàíäìàóýðå:
    ˆ

    YAML

    # app/config/security.yml
    security:
    firewalls:
    main:
    # ...
    switch_user: true
    2.12.

    Áåçîïàñíîñòü

    243

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    XML









    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'main'=> array(
    // ...
    'switch_user' => true
    ),
    ),
    ));

    Äëÿ ïåðåêëþ÷åíèÿ íà äðóãîãî ïîëüçîâàòåëÿ ïðîñòî äîáàâüòå â ñòðîêó çàïðîñà òåêóùåãî
    URL ïàðàìåòð _switch_user è èìÿ ïîëüçîâàòåëÿ:
    http://example.com/somewhere?_switch_user=thomas
    Äëÿ òîãî, ÷òîáû ïåðåêëþ÷èòüñÿ îáðàòíî, èñïîëüçóéòå ñïåöèàëüíîå èìÿ _exit:
    http://example.com/somewhere?_switch_user=_exit
    Åñòåñòâåííî, òàêàÿ âîçìîæíîñòü äîëæíà áûòü äîñòóïíà íåáîëüøîé ãðóïïå
    ïîëüçîâàòåëåé. Ïî óìîë÷àíèþ, ýòà ôóíêöèÿ äîñòóïíà ïîëüçîâàòåëÿì ñ ðîëüþ
    ROLE_ALLOWED_TO_SWITCH. Íàèìåíîâàíèå ýòîé ðîëè ìîæíî èçìåíèòü ïðè ïîìîùè
    îïöèè role. Äëÿ áîëüøåé áåçîïàñíîñòè âû òàêæå ìîæåòå èçìåíèòü íàèìåíîâàíèÿ
    ïàðàìåòðà äëÿ ñòðîêè çàïðîñà ïðè ïîìîùè îïöèè parameter:
    ˆ

    YAML

    # app/config/security.yml
    security:
    firewalls:
    main:
    // ...
    switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }

    ˆ

    244

    XML

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0









    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'main'=> array(
    // ...
    'switch_user' => array('role' => 'ROLE_ADMIN', 'parameter' => '_want_to_be_this
    ),
    ),
    ));

    2.12.11 Àóòåíòèôèêàöèÿ áåç ñîõðàíåíèÿ ñîñòîÿíèÿ (stateless)

    Ïî óìîë÷àíèþ, Symfony2 èñïîëüçóåò êóêè (ñåññèþ) äëÿ õðàíåíèÿ êîíòåêñòà áåçîïàñíîñòè ïîëüçîâàòåëÿ. Íî, åñëè âû èñïîëüçóåòå, ê ïðèìåðó, ñåðòèôèêàòû èëè HTTP àóòåíòèôèêàöèþ, ñîõðàíåíèå íå òðåáóåòñÿ, òàê êàê àâòîðèçàöèîííûå äàííûå äîñòóïíû äëÿ
    êàæäîãî çàïðîñà.  ýòîì ñëó÷àå, è åñëè âû íå õîòèòå ñîõðàíÿòü ÷òî-ëèáî ìåæäó çàïðîñàìè, âû ìîæåòå àêòèâèðîâàòü stateless àóòåíòèôèêàöèþ (áåç ñîõðàíåíèÿ ñîñòîÿíèÿ,
    ò.å. Symfony2 íå áóäåò ñîçäàâàòü êóêè):
    ˆ

    YAML

    # app/config/security.yml
    security:
    firewalls:
    main:
    http_basic: ~
    stateless: true

    ˆ

    XML







    2.12.

    Áåçîïàñíîñòü

    245

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    // app/config/security.php
    $container->loadFromExtension('security', array(
    'firewalls' => array(
    'main' => array('http_basic' => array(), 'stateless' => true),
    ),
    ));

    Ïðèìå÷àíèå: Åñëè âû èñïîëüçóåòå ôîðìó ëîãèíà, Symfony2 áóäåò ñîçäàâàòü êóêè
    âñåãäà, äàæå åñëè stateless èìååò çíà÷åíèå true.

    2.12.12 Çàêëþ÷åíèå

    Áåçîïàñíîñòü ìîæåò áûòü âåñüìà ñëîæíûì âîïðîñîì äëÿ ðåøåíèÿ åãî â âàøåì ïðèëîæåíèè. Ê ñ÷àñòüþ, êîìïîíåíò áåçîïàñíîñòè Symfony ñëåäóåò õîðîøî çàðåêîìåíäîâàâøåé ñåáÿ ìîäåëè, îñíîâàííîé íà àóòåíòèôèêàöèè è àâòîðèçàöèè. Àóòåíòèôèêàöèÿ,
    êîòîðàÿ âñåãäà èä¼ò ïåðâîé, îáðàáàòûâàåòñÿ áðàíäìàóýðîì, ÷üÿ ðàáîòà çàêëþ÷àåòñÿ
    óñòàíîâèòü ëè÷íîñòü ïîëüçîâàòåëÿ ïðè ïîìîùè ëþáîãî èç äîñòóïíûõ ìåòîäîâ (HTTP
    àóòåíòèôèêàöèÿ, ôîðìà ëîãèíà è ò.ä.). Â êíèãå ðåöåïòîâ âû òàêæå íàéä¼òå ïðèìåðû
    äðóãèõ ìåòîäîâ àóòåíòèôèêàöèè, âêëþ÷àÿ òî, êàê ðåàëèçîâàòü ôóíêöèþ çàïîìíèòü
    ìåíÿ ïðè ïîìîùè êóêè.
    Ïîñëå òîãî êàê ïîëüçîâàòåëü àóòåíòèôèöèðîâàí, àâòîðèçàöèÿ ìîæåò îïðåäåëèòü èìååò
    ëè îí äîñòóï ê òîìó èëè èíîìó ðåñóðñó. Êàê ïðàâèëî, ê URL, êëàññàì èëè ìåòîäàì
    ñòàâÿòñÿ â ñîîòâåòñòâèå íåêîòîðûå ðîëè è åñëè ïîëüçîâàòåëü íå èìååò òðåáóåìîé ðîëè,
    äîñòóï åìó áóäåò çàïðåù¼í. Òåì íå ìåíåå, àâòîðèçàöèÿ ýòî áîëåå ñëîæíûé ìåõàíèçì,
    ñëåäóþùèé ñèñòåìå ãîëîñîâàíèÿ, áëàãîäàðÿ êîòîðîé ìíîæåñòâî ðàçíûõ ó÷àñòíèêîâ
    ìîãóò îïðåäåëèòü èìååò ëè ïîëüçîâàòåëü ïðàâà äîñòóïà ê ðåñóðñó èëè æå íåò.
    2.12.13 ×èòàéòå òàêæå â êíèãå ðåöåïòîâ

    ˆ Ôîðñèðîâàíèå HTTP/HTTPS
    ˆ Áëýêëèñòèíã ïîëüçîâàòåëÿ ïî IP ïðè ïîìîùè custom voter
    ˆ Ñïèñêè êîíòðîëÿ äîñòóïà (ACLs)
    ˆ /cookbook/security/remember_me

    246

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    2.13 HTTP Êýøèðîâàíèå
    Ïðèðîäà íàñûùåííûõ (áîãàòûõ) âåá-ïðèëîæåíèé ïîäðàçóìåâàåò, ÷òî îíè äèíàìè÷åñêèå.
    Âíå çàâèñèìîñòè îò òîãî, íàñêîëüêî ýôôåêòèâíî âàøå ïðèëîæåíèå, êàæäûé çàïðîñ
    áóäåò ñîäåðæàòü ðàáîòû áîëüøå ÷åì îòäà÷à ïðîñòîãî ñòàòè÷åñêîãî ôàéëà.
    È äëÿ áîëüøèíñòâà âåá-ïðèëîæåíèé ýòî âïîëíå íîðìàëüíî. Symfony2 î÷åíü áûñòð è,
    åñëè âû íå äåëàåòå ÷åãî-òî äåéñòâèòåëüíî òÿæåëîâåñíîãî, êàæäûé çàïðîñ áóäåò îáðàáàòûâàòüñÿ áûñòðî è íå ñîçäàâàÿ ñòðåññîâûõ ñèòóàöèé íà ñåðâåðå.
    Íî, ïî ìåðå ðîñòà âàøåãî ñàéòà, ðîñò íàãðóçêè ìîæåò ñòàòü ïðîáëåìîé. Ðàáîòà, êîòîðàÿ îáû÷íî âûïîëíÿåòñÿ äëÿ êàæäîãî çàïðîñà, òåïåðü äîëæíà áûòü âûïîëíåíà òîëüêî
    åäèíîæäû. È ýòî èìåííî òî, ÷åãî ïîçâîëÿåò äîáèòüñÿ êýøèðîâàíèå.
    2.13.1 Êýøèðîâàíèå íà ïëå÷àõ ãèãàíòîâ

    Íàèáîëåå ýôôåêòèâíûì ñïîñîáîì óâåëè÷èòü áûñòðîäåéñòâèå ïðèëîæåíèÿ ÿâëÿåòñÿ êýøèðîâàíèå ñòðàíèöû öåëèêîì è çàòåì, â îáõîä ïðèëîæåíèÿ, îòäàâàòü êýøèðîâàííûå
    äàííûå äëÿ êàæäîãî çàïðîñà. Êîíå÷íî æå, ýòî íå âñåãäà âîçìîæíî ïðèìåíèòü, îñîáåííî äëÿ î÷åíü äèíàìè÷íî ìåíÿþùèõñÿ ñàéòîâ... èëè âñ¼ æå âîçìîæíî?  ýòîé ãëàâå âû
    óâèäèòå, êàê ðàáîòàåò ñèñòåìà êýøèðîâàíèÿ Symfony2 è ïî÷åìó ìû ñ÷èòàåì ýòî íàèëó÷øèì ðåøåíèåì èç âîçìîæíûõ.
    Ñèñòåìà êýøèðîâàíèÿ Symfony2 îòëè÷àåòñÿ îò äðóãèõ, òàê êàê îíà ïîëàãàåòñÿ íà ïðîñòîòó è ìîùü HTTP êýøèðîâàíèÿ, êàê ýòî îïðåäåëåíî â ñïåöèôèêàöèè HTTP (ñì.
    Ñïåöèôèêàöèÿ ïðîòîêîëà HTTP ). Âìåñòî òîãî ÷òîáû èçîáðåòàòü êýøèðîâàíèå çàíîâî,
    Symfony2 ïîëüçóåòñÿ ñòàíäàðòîì, êîòîðûé îïðåäåëÿåò áàçîâûå êîììóíèêàöèè â Web.
    Êàê òîëüêî âû ïîéì¼òå îñíîâîïîëàãàþùèå ìîäåëè HTTP âàëèäàöèè è èñòå÷åíèÿ ñðîêà
    äëÿ êýøà, âû áóäåòå ãîòîâû ê óïðàâëåíèþ ñèñòåìîé êýøèðîâàíèÿ Symfony2.
    Ñ öåëüþ èçó÷åíèÿ òîãî, êàê êýøèðîâàòü â Symfony2, ìû ïðîéä¼ì ÷åòûðå øàãà:
    ˆ Øàã 1: êýøèðóþùèé øëþç , èëè îáðàòíûé ïðîêñè-ñåðâåð (reverse proxy), ýòî
    íåçàâèñèìûé ñëîé, êîòîðûé ðàñïîëàãàåòñÿ ïåðåä âàøèì ïðèëîæåíèåì. Îáðàòíûé
    ïðîêñè êýøèðóåò îòâåòû ïî ìåðå èõ ïîñòóïëåíèÿ îò ïðèëîæåíèÿ è îòâå÷àåò íà çàïðîñû ïðè ïîìîùè êýøèðîâàííûõ îòâåòîâ, íå ïîäêëþ÷àÿ ïðèëîæåíèå. Symfony2
    ñîäåðæèò ñâîé ñîáñòâåííûé îáðàòíûé ïðîêñè, íî âû òàêæå ìîæåòå èñïîëüçîâàòü
    ëþáîé îáðàòíûé ïðîêñè íà âàø âûáîð.
    ˆ Øàã 2: çàãîëîâêè HTTP êýøà èñïîëüçóþòñÿ äëÿ êîììóíèêàöèè êýøèðóþùåãî
    øëþçà è ëþáîãî äðóãîãî êýøåðà, êîòîðûé ìîæåò íàõîäèòüñÿ ìåæäó âàøèì ïðèëîæåíèåì è êëèåíòîì. Symfony2 ñîäåðæèò òèïîâóþ êîíôèãóðàöèþ ïî óìîë÷àíèþ
    è ìîùíûé èíòåðôåéñ äëÿ ðàáîòû ñ çàãîëîâêàìè êýøà.
    ˆ Øàã 3: îêîí÷àíèå ñðîêà äåéñòâèÿ è âàëèäàöèÿ HTTP êýøà - ýòî äâå ìîäåëè,
    èñïîëüçóåìûå äëÿ îïðåäåëåíèÿ ÿâëÿåòñÿ ëè êýøèðîâàííûé êîíòåíò ñâåæèì (è

    2.13.

    HTTP Êýøèðîâàíèå

    247

    Symfony Documentation, Âûïóñê 2.0

    ìîæåò ïîâòîðíî áðàòüñÿ èç êýøà) èëè æå
    ñîçäàòü ïðè ïîìîùè ïðèëîæåíèÿ).

    ïðîñðî÷åííûì

    (è åãî íåîáõîäèìî ïåðå-

    ˆ Øàã 4: Edge Side Includes (ESI) ïîçâîëÿþò èñïîëüçîâàòü HTTP êýø äëÿ íåçàâèñèìîãî êýøèðîâàíèÿ ôðàãìåíòîâ ñòðàíèö (äàæå âëîæåííûõ ôðàãìåíòîâ). Ïðè
    ïîìîùè ESI âû ìîæåòå êýøèðîâàòü âñþ ñòðàíèöó íà 60 ìèíóò, íî âñòðîåííóþ
    áîêîâóþ ïàíåëü ëèøü íà 5 ìèíóò.
    Òàê êàê HTTP êýøèðîâàíèå íå ÿâëÿåòñÿ äîñòîÿíèåì ëèøü Symfony, ñóùåñòâóåò ìíîæåñòâî ñòàòåé ïî äàííîé òåìå. Åñëè âû íîâè÷îê â HTTP êýøèðîâàíèè, ìû íàñòîÿòåëüíî
    ðåêîìåíäóåì âàì ïðî÷èòàòü ñòàòüþ Ryan Tomayko : Things Caches Do. Äðóãèì èñ÷åðïûâàþùèì ðóêîâîäñòâîì ÿâëÿåòñÿ Cache Tutorial îò Mark Nottingham.
    2.13.2 Êýøèðîâàíèå ïðè ïîìîùè êýøèðóþùåãî øëþçà

    Ïðè êýøèðîâàíèè ïðè ïîìîùè HTTP, êýø ïîëíîñòüþ îòäåë¼í îò âàøåãî ïðèëîæåíèÿ
    è ðàñïîëàãàåòñÿ ìåæäó âàøèì ïðèëîæåíèåì è êëèåíòîì, âûïîëíèâøåì çàïðîñ.
    Ðàáîòà êýøà çàêëþ÷àåòñÿ â ïðè¼ìå çàïðîñà îò êëèåíòà è ïåðåäà÷å åãî âàøåìó ïðèëîæåíèþ. Êýø òàêæå áóäåò ïîëó÷àòü îòâåò îò âàøåãî ïðèëîæåíèÿ è ïåðåíàïðàâëÿòü
    åãî äàëåå ê êëèåíòó. Êýø ÿâëÿåòñÿ ïîñðåäíèêîì â êëèåíò-ñåðâåðíûõ êîììóíèêàöèÿõ
    ìåæäó êëèåíòîì è âàøèì ïðèëîæåíèåì.
    Ïî ïóòè, êýø áóäåò ñîõðàíÿòü êàæäûé îòâåò, êîòîðûé ïîëàãàåò êýøèðóåìûì (ñì.
    Ââåäåíèå â HTTP êýøèðîâàíèå ). Åñëè ýòîò æå ðåñóðñ áóäåò çàïðîøåí åù¼ ðàç, êýø
    îòïðàâèò ñîõðàí¼ííûé (êýøèðîâàííûé) îòâåò êëèåíòó, èãíîðèðóÿ âàøå ïðèëîæåíèå.
    Ýòîò òèï êýøèðîâàíèÿ èçâåñòåí ïîä èìåíåì êýøèðóþùåãî HTTP øëþçà. Ñóùåñòâóåò
    ìíîãî êýøåðîâ òàêîãî òèïà, íàïðèìåð: Varnish, Squid â ðåæèìå îáðàòíîãî ïðîêñè, à
    òàêæå îáðàòíûé ïðîêñè Symfony2.
    Òèïû êýøèðîâàíèÿ

    Íî êýøèðóþùèì øëþçîì òèïû êýøåðîâ íå èñ÷åðïûâàþòñÿ. Ôàêòè÷åñêè, çàãîëîâêè
    HTTP êýøà, îòïðàâëÿåìûå âàøèì ïðèëîæåíèåì, ìîãóò áûòü ïîëó÷åíû è èñïîëüçîâàíû
    òðåìÿ ðàçëè÷íûìè òèïàìè êýøåðîâ:
    ˆ

    Êýø áðàóçåðà : Êàæäûé áðàóçåð èìååò ñâîé ñîáñòâåííûé ëîêàëüíûé êýø, êîòîðûé
    â îñíîâíîì èñïîëüçóåòñÿ, êîãäà âû íàæèìàåòå êíîïêó back, à òàêæå êýø êàðòèíîê è ïðî÷èõ ðåñóðñîâ. Êýø áðàóçåðà - ýòî ëè÷íûé êýø, êîòîðûé íå èñïîëüçóåòñÿ
    íèêåì áîëåå.

    ˆ

    Êýøèðóþùèå ïðîêñè :

    248

    Ïðîêñè - ýòî êýø îáùåãî äîñòóïà, òàê êàê çà îäíèì òàêèì
    ïðîêñè ìîæåò íàõîäèòüñÿ ìíîãî êëèåíòîâ. Òàêèå ïðîêñè êàê ïðàâèëî óñòàíàâëèâàþòñÿ áîëüøèìè êîìïàíèÿìè è Èíòåðíåò-ïðîâàéäåðîì äëÿ óìåíüøåíèÿ âðåìåíè
    äîñòóïà ê ðåñóðñàì è ñíèæåíèþ ñåòåâîãî òðàôèêà.

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    Êýøèðóþùèå øëþçû :

    Êàê è ïðîêñè, îíè òàêæå ïðåäñòàâëÿþò ñîáîé êýø îáùåãî
    äîñòóïà, íî íà ñòîðîíå ñåðâåðà. Óñòàíàâëèâàåìûå àäìèíèñòðàòîðàìè, îíè äåëàþò
    ñàéòû áîëåå ìàñøòàáèðóåìûìè, íàä¼æíûìè è áûñòðûìè.

    Ñîâåò: Êýøèðóþùèå øëþçû èíîãäà íàçûâàþò êýøèðóþùèìè îáðàòíûìè ïðîêñè, ñóððîãàòíûìè êýøåðàìè è äàæå HTTP àêñåëåðàòîðàìè.

    Ïðèìå÷àíèå: Çíà÷èìîñòü

    ëè÷íîãî êýøà ïî ñðàâíåíèþ ñ êýøåì îáùåãî äîñòóïà ñòàíîâèòñÿ áîëåå çàìåòíîé, åñëè ìû ãîâîðèì î êýøèðîâàíèè îòâåòîâ, ñîäåðæàùèõ êîíòåíò,
    îòíîñÿùèéñÿ ê êîíêðåòíîìó ïîëüçîâàòåëþ (íàïðèìåð, èíôîðìàöèÿ î ñ÷¼òå).

    Êàæäûé îòâåò îò âàøåãî ïðèëîæåíèÿ áóäåò ïðîõîäèòü ÷åðåç ïåðâûé òèï êýøà èëè æå
    ÷åðåç îáà - ïåðâûé è âòîðîé. Ýòè êýøè íàõîäÿòñÿ âíå âàøåãî êîíòðîëÿ, íî ñëåäóþò
    óêàçàíèÿì äëÿ HTTP êýøà, êîòîðûå åñòü â îòâåòå.
    Îáðàòíûé ïðîêñè Symfony2

    Symfony2 ñîäåðæèò îáðàòíûé ïðîêñè (òàêæå íàçûâàåìûé êýøèðóþùèì øëþçîì), íàïèñàííûé íà PHP. Àêòèâèðóéòå åãî è êýøèðóåìûå îòâåòû âàøåãî ïðèëîæåíèÿ íà÷íóò
    êýøèðîâàòüñÿ íàäëåæàùèì îáðàçîì. Åãî óñòàíîâêà î÷åíü ïðîñòà. Êàæäîå íîâîå ïðèëîæåíèå Symfony2 ñîäåðæèò óæå íàñòðîåííîå êýøèðóþùåå ÿäðî (AppCache), êîòîðîå
    ñëóæèò îáîëî÷êîé äëÿ ÿäðà ïî óìîë÷àíèþ (AppKernel). Êýøèðóþùåå ÿäðî è åñòü òîò
    ñàìûé îáðàòíûé ïðîêñè.
    Äëÿ òîãî ÷òîáû àêòèâèðîâàòü êýøèðîâàíèå, ìîäèôèöèðóéòå êîä ôðîíò-êîíòðîëëåðà
    òàêèì îáðàçîì, ÷òîáû îí èñïîëüçîâàë êýøèðóþùåå ÿäðî:
    // web/app.php
    require_once __DIR__.'/../app/bootstrap.php.cache';
    require_once __DIR__.'/../app/AppKernel.php';
    require_once __DIR__.'/../app/AppCache.php';
    use Symfony\Component\HttpFoundation\Request;
    $kernel = new AppKernel('prod', false);
    $kernel->loadClassCache();
    // wrap the default AppKernel with the AppCache one
    $kernel = new AppCache($kernel);
    $kernel->handle(Request::createFromGlobals())->send();

    Êýøèðóþùåå ÿäðî íåìåäëåííî íà÷í¼ò äåéñòâîâàòü â êà÷åñòâå îáðàòíîãî ïðîêñè - áóäåò
    êýøèðîâàòü îòâåòû âàøåãî ïðèëîæåíèÿ è îòïðàâëÿòü èõ êëèåíòó.
    2.13.

    HTTP Êýøèðîâàíèå

    249

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Êýøèðóþùåå ÿäðî èìååò îñîáûé ìåòîä getLog(), êîòîðûé âîçâðàùàåò ñòðîêî-

    âîå ïðåäñòàâëåíèå òîãî, ÷òî ïðîèñõîäèò íà êýøèðóþùåì óðîâíå. Â dev îêðóæåíèè âû
    ìîæåòå èñïîëüçîâàòü åãî äëÿ îòëàäêè è ïðîâåðêè âàøåé ñòðàòåãèè êýøèðîâàíèÿ:

    error_log($kernel->getLog());

    Îáúåêò AppCache èìååò êîíôèãóðàöèþ ïî óìîë÷àíèþ, íî âû ìîæåòå êîíôèãóðèðîâàòü
    è íàñòðàèâàòü åãî îïöèè ïîñðåäñòâîì ïåðåîïðåäåëåíèÿ ìåòîäà getOptions():
    // app/AppCache.php
    class AppCache extends Cache
    {
    protected function getOptions()
    {
    return array(
    'debug'
    'default_ttl'
    'private_headers'
    'allow_reload'
    'allow_revalidate'
    'stale_while_revalidate'
    'stale_if_error'
    );
    }
    }

    =>
    =>
    =>
    =>
    =>
    =>
    =>

    false,
    0,
    array('Authorization', 'Cookie'),
    false,
    false,
    2,
    60,

    Ñîâåò: Äëÿ èçìåíåíèÿ îïöèè debug ïåðåîïðåäåëÿòü getOptions() íå îáÿçàòåëüíî, òàê
    êàê îíà àâòîìàòè÷åñêè ïðèíèìàåò çíà÷åíèå ïàðàìåòðà debug îò AppKernel.

    Íèæå ïðåäñòàâëåí ñïèñîê îñíîâíûõ îïöèé:
    ˆ default_ttl: Âðåìÿ (â ñåêóíäàõ), â òå÷åíèå êîòîðîãî êýøèðîâàííûé ýëåìåíò ñ÷èòàåòñÿ ñâåæèì, åñëè îòâåò íå ñîäåðæèò òî÷íûõ äàííûõ î åãî ñâåæåñòè. ßâíî
    óêàçàííûå çàãîëîâêè Cache-Control èëè Expires ïåðåçàïèñûâàþò ýòî çíà÷åíèå
    (ïî óìîë÷àíèþ 0);
    ˆ private_headers: Íàáîð çàãîëîâêîâ çàïðîñà, êîòîðûå àêòèâèðóþò ïðèâàòíûé
    Cache-Control äëÿ îòâåòîâ, êîòîðûå ÿâíî íå óêàçûâàþò ïîâåäåíèå ïðèâàòíûé èëè ïóáëè÷íûé ïîñðåäñòâîì äèðåêòèâû Cache-Control (ïî óìîë÷àíèþ
    Authorization è Cookie);
    ˆ allow_reload: Îïðåäåëÿåò, ìîæåò ëè êëèåíò ôîðñèðîâàòü îáíîâëåíèå êýøà ïðè
    ïîìîùè äèðåêòèâû Cache-Control no-cache â çàïðîñå. Óñòàíîâèòå å¼ â true äëÿ
    ñëåäîâàíèÿ ñïåöèôèêàöèè RFC 2616 (ïî óìîë÷àíèþ false);
    250

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ allow_revalidate: Îïðåäåëÿåò, ìîæåò ëè êëèåíò ôîðñèðîâàòü ïåðåïðîâåðêó êýøà
    ïðè ïîìîùè äèðåêòèâû Cache-Control max-age=0 â çàïðîñå. Óñòàíîâèòå å¼ â
    true äëÿ ñëåäîâàíèÿ ñïåöèôèêàöèè RFC 2616 (ïî óìîë÷àíèþ false);
    ˆ stale_while_revalidate: Îïðåäåëÿåò ÷èñëî ñåêóíä ïî óìîë÷àíèþ (êâàíòèôèêàöèÿ âðåìåíè ïðîèçâîäèòñÿ â ñåêóíäàõ, òàê êàê TTL (time to live) îòâåòà èçìåðÿåòñÿ
    â ñåêóíäàõ) âî âðåìÿ êîòîðîãî êýø áóäåò íåìåäëåííî âîçâðàùàòü ïðîñðî÷åííûé
    îòâåò, ïîêà ïðîèçâîäèòñÿ åãî ôîíîâàÿ ïåðåïðîâåðêà (ïî óìîë÷àíèþ 2); ýòà îïöèÿ
    ïåðåîïðåäåëÿåòñÿ ðàñøèðåíèåì HTTP Cache-Control - stale-while-revalidate
    (ñì. RFC 5861);
    ˆ stale_if_error: Îïðåäåëÿåò ÷èñëî ñåêóíä ïî óìîë÷àíèþ (êâàíòèôèêàöèÿ âðåìåíè ïðîèçâîäèòñÿ â ñåêóíäàõ, òàê êàê TTL (time to live) îòâåòà èçìåðÿåòñÿ â
    ñåêóíäàõ), âî âðåìÿ êîòîðîãî êýø ìîæåò îáñëóæèâàòü ïðîñðî÷åííûé îòâåò, åñëè
    âîçíèêàåò îøèáêà (ïî óìîë÷àíèþ 60). Ýòà îïöèÿ ïåðåîïðåäåëÿåòñÿ ðàñøèðåíèåì
    HTTP Cache-Control - stale-if-error (ñì. RFC 5861)
    Åñëè debug èìååò çíà÷åíèå true, Symfony2 àâòîìàòè÷åñêè äîáàâëÿåò â îòâåò çàãîëîâîê
    X-Symfony-Cache, ñîäåðæàùèé ïîëåçíóþ èíôîðìàöèþ î ÷èñëå ñðàáàòûâàíèé êýøà è î
    ÷èñëå íå íàéäåííûõ îòâåòîâ â êýøå.

    Èñïîëüçîâàíèå äðóãîãî îáðàòíîãî ïðîêñè
    Îáðàòíûé ïðîêñè Symfony2 ýòî îòëè÷íûé èíñòðóìåíò äëÿ èñïîëüçîâàíèÿ âî âðåìÿ
    ðàçðàáîòêè èëè æå ïðè âûãðóçêå âàøåãî ñàéòà íà âèðòóàëüíûé (øàðåä) õîñòèíã,
    ãäå âû íå ìîæåòå óñòàíîâèòü íè÷åãî, êðîìå PHP êîäà. Íî, ïðîêñè íà PHP íèêîãäà
    íå áóäåò áûñòðåå ïðîêñè íà Ñè. Âîò ïî÷åìó ìû íàñòîÿòåëüíî ðåêîìåíäóåì âàì èñïîëüçîâàòü Varnish èëè Squid íà âàøèõ ïðîäóêòîâûõ ñåðâåðàõ, åñëè ýòî âîçìîæíî.
    Õîðîøåé íîâîñòüþ äëÿ âàñ áóäåò òî, ÷òî ïåðåêëþ÷åíèå ñ îäíîãî ïðîêñè ñåðâåðà íà
    äðóãîé âûïîëíÿåòñÿ ïðîñòî è ïðîçðà÷íî è íå òðåáóåò ìîäèôèêàöèè êîäà âàøåãî
    ïðèëîæåíèÿ. Ïðîñòî íà÷íèòå ðàáîòó ñ îáðàòíûì ïðîêñè Symfony2 è çàìåíèòå åãî
    íà Varnish, êîãäà òðàôèê âîçðàñò¼ò.
    Áîëüøå îá èñïîëüçîâàíèè Varnish ñ Symfony2 ÷èòàéòå â êíèãå ðåöåïòîâ: Êàê èñïîëüçîâàòü Varnish .

    Ïðèìå÷àíèå: Áûñòðîäåéñòâèå îáðàòíîãî ïðîêñè Symfony2 íå çàâèñèò îò ñëîæíîñòè

    ïðèëîæåíèÿ. Ýòî äîñòèãàåòñÿ çà ñ÷¼ò òîãî, ÷òî ÿäðî ïðèëîæåíèÿ çàãðóæàåòñÿ ëèøü â
    òîì ñëó÷àå, êîãäà ê íåìó òðåáóåòñÿ ïåðåíàïðàâèòü âõîäÿùèé çàïðîñ.

    2.13.3 Ââåäåíèå â HTTP êýøèðîâàíèå

    Äëÿ òîãî, ÷òîáû ïîëó÷èòü ïîëüçó îò êýøèðîâàíèÿ, âàøå ïðèëîæåíèå äîëæíî èìåòü
    âîçìîæíîñòü ñîîáùèòü, êàêèå îòâåòû ìîãóò áûòü êýøèðîâàíû, à òàêæå ïðàâèëà, êî-

    2.13.

    HTTP Êýøèðîâàíèå

    251

    Symfony Documentation, Âûïóñê 2.0

    òîðûå áóäóò óêàçûâàòü êîãäà è êàê èñòåêàåò ñðîê äåéñòâèÿ ýòîãî êýøà. Ýòîãî ìîæíî
    äîñòè÷ü ïðè ïîìîùè HTTP çàãîëîâêîâ äëÿ êýøèðîâàíèÿ îòâåòîâ.

    Ñîâåò: Èìåéòå â âèäó, ÷òî HTTP ýòî íå áîëåå ÷åì ÿçûê (ïðîñòîé òåêñòîâûé ÿçûê),

    êîòîðûé âåá êëèåíòû (íàïðèìåð, áðàóçåðû) è âåá ñåðâåðû èñïîëüçóþò äëÿ êîììóíèêàöèé ìåæäó ñîáîé. Êîãäà ìû ãîâîðèì îá HTTP êýøèðîâàíèè, ìû ãîâîðèì î ÷àñòè
    ýòîãî ÿçûêà, êîòîðàÿ ïîçâîëÿåòñÿ êëèåíòàì è ñåðâåðàì îáìåíèâàòüñÿ èíôîðìàöèåé,
    îòíîñÿùåéñÿ ê êýøèðîâàíèþ.
    Ñïåöèôèêàöèÿ HTTP ñîäåðæèò ÷åòûðå çàãîëîâêà, îòíîñÿùèõñÿ ê êýøèðîâàíèþ:
    ˆ Cache-Control
    ˆ Expires
    ˆ ETag
    ˆ Last-Modified
    Íàèáîëåå âàæíûì è ìíîãîñòîðîííèì ÿâëÿåòñÿ çàãîëîâîê Cache-Control, êîòîðûé íà
    ñàìîì äåëå ÿâëÿåòñÿ êîëëåêöèåé ðàçíîîáðàçíîé èíôîðìàöèè î êýøèðîâàíèè.

    Ïðèìå÷àíèå: Êàæäûé èç çàãîëîâêîâ áóäåò äåòàëüíî ðàññìîòðåí â ñåêöèè

    Ìîäåëè

    êýøèðîâàíèÿ â HTTP: expiration è validation .

    Çàãîëîâîê Cache-Control

    Çàãîëîâîê Cache-Control óíèêàëåí çà ñ÷¼ò òîãî, ÷òî îí ñîäåðæèò íå îäíî êîíêðåòíîå
    çíà÷åíèå, à ìíîãî ðàçëè÷íûõ äàííûõ î êýøèðóåìîñòè îòâåòà. Êàæäàÿ íîâàÿ ïîðöèÿ
    äàííûõ îòäåëÿåòñÿ çàïÿòîé:
    Cache-Control: private, max-age=0, must-revalidate
    Cache-Control: max-age=3600, must-revalidate
    Symfony

    ïðåäîñòàâëÿåò
    Cache-Control:

    ìåòîäû

    äëÿ

    áîëåå

    óäîáíîãî

    óïðàâëåíèÿ

    çàãîëîâêîì

    //...
    $response = new Response();
    // ïîìåòèòü îòâåò êàê public èëè private
    $response->setPublic();
    $response->setPrivate();

    252

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // óñòàíîâèòü max age äëÿ private è shared îòâåòîâ
    $response->setMaxAge(600);
    $response->setSharedMaxAge(600);
    // óñòàíîâèòü ñïåöèàëüíóþ äèðåêòèâó Cache-Control
    $response->headers->addCacheControlDirective('must-revalidate', true);

    Ïóáëè÷íûå (public) vs ×àñòíûå (private) îòâåòû

    Êýøèðóþùèå øëþçû è ïðîêñè ðàññìàòðèâàþò îáùèå êýøè êàê êýøèðîâàííûé êîíòåíò, êîòîðûé èñïîëüçóåòñÿ áîëåå ÷åì îäíèì ïîëüçîâàòåëåì. Åñëè áóäåò ñëó÷àéíî ñîõðàí¼í îòâåò, ñïåöèôè÷íûé äëÿ îòäåëüíîãî ïîëüçîâàòåëÿ, âïîñëåäñòâèè îí ìîæåò áûòü
    îòïðàâëåí ìíîæåñòâó ðàçëè÷íûõ ïîëüçîâàòåëåé. Ïðåäñòàâüòå, ÷òî èíôîðìàöèÿ î âàøåì
    ñ÷¼òå áûëà êýøèðîâàíà è áóäåò îòïðàâëåíà ëþáîìó ïîëüçîâàòåëþ, êîòîðûé çàïðîñèò
    ñâîþ ñîáñòâåííóþ ñòðàíèöó ñî ñ÷¼òîì!
    Äëÿ òîãî ÷òîáû êîððåêòíî îáðàáîòàòü ýòó ñèòóàöèþ, êàæäûé îòâåò ìîæåò áûòü îáúÿâëåí ïóáëè÷íûì èëè æå ÷àñòíûì:
    ˆ

    public :

    ˆ

    private :

    øàìè;

    Ïóáëè÷íûé îòâåò ìîæåò êýøèðîâàòüñÿ êàê ÷àñòíûì, òàê è ïóáëè÷íûì êý-

    ×àñòíûé îòâåò ïîäðàçóìåâàåò ÷òî îí öåëèêîì èëè æå åãî ÷àñòü ïðåäíàçíà÷åíà äëÿ îäíîãî åäèíñòâåííîãî ïîëüçîâàòåëÿ è íå äîëæåí êýøèðîâàòüñÿ ïóáëè÷íûìè êýøåðàìè.

    Symfony äåéñòâóåò êîíñåðâàòèâíî è ïîìå÷àåò êàæäûé îòâåò êàê ÷àñòíûé. Äëÿ òîãî
    ÷òîáû ïîëó÷èòü ïðåèìóùåñòâà îò èñïîëüçîâàíèÿ ïóáëè÷íûõ êýøåðîâ (â òîì ÷èñëå è
    îáðàòíîãî ïðîêñè Symfony2), îòâåò äîëæåí áûòü ïîìå÷åí êàê ïóáëè÷íûé (public).
    Áåçîïàñíûå ìåòîäû

    HTTP êýøèðîâàíèå ðàáîòàåò ëèøü äëÿ áåçîïàñíûõ HTTP ìåòîäîâ (òàêèõ êàê GET
    è HEAD). Ïîä áåçîïàñíîñòüþ ýòèõ ìåòîäîâ ïîíèìàåòñÿ, ÷òî âû íèêîãäà íå èçìåíèòå
    ñîñòîÿíèå ïðèëîæåíèÿ ïðè îáðàáîòêå òàêèõ çàïðîñîâ (ïðè ýòîì âû, êîíå÷íî, ìîæåòå
    ëîããèðîâàòü èíôîðìàöèþ, êýøèðîâàòü äàííûå è ò.ä.). Ýòî îãðàíè÷åíèå èìååò äâà ñëåäñòâèÿ:
    ˆ Âû íèêîãäà íå äîëæíû èçìåíÿòü ñîñòîÿíèå âàøåãî ïðèëîæåíèÿ, îòâå÷àÿ íà GET
    èëè HEAD çàïðîñ. Äàæå åñëè âû íå èñïîëüçóåòå êýøèðóþùèé øëþç, íàëè÷èå
    ïðîêñè-êýøà îçíà÷àåò, ÷òî ëþáîé GET èëè HEAD çàïðîñ ìîæåò êàê ïîïàñòü â
    âàøå ïðèëîæåíèå, òàê è íå ïîïàñòü (ïðîêñè âåðí¼ò êýøèðîâàííûå äàííûå, íå
    çàòðàãèâàÿ ïðèëîæåíèå).
    ˆ Íè â êîåì ñëó÷àå íå êýøèðóéòå PUT, POST è DELETE ìåòîäû. Ýòè ìåòîäû ïðåäíàçíà÷åíû äëÿ èçìåíåíèÿ ñîñòîÿíèÿ ïðèëîæåíèÿ (íàïðèìåð, óäàëåíèÿ çàïèñè èç
    2.13.

    HTTP Êýøèðîâàíèå

    253

    Symfony Documentation, Âûïóñê 2.0

    áëîãà). Åñëè èõ êýøèðîâàòü, òî ÷àñòü çàïðîñîâ íà èçìåíåíèå ñîñòîÿíèÿ ïðèëîæåíèÿ íå áóäóò äîñòèãàòü åãî.
    Ïðàâèëà êýøèðîâàíèÿ è çíà÷åíèÿ ïî óìîë÷àíèþ

    HTTP 1.1 ïî óìîë÷àíèþ ðàçðåøàåò êýøèðîâàíèå, åñëè ÿâíî íå óêàçàí çàãîëîâîê
    Cache-Control. Íà ïðàêòèêå, áîëüøèíñòâî êýøåðîâ íè÷åãî íå äåëàþò, åñëè çàïðîñû
    èìåþò êóêè, àâòîðèçàöèîííûé çàãîëîâîê, èñïîëüçóþò íåáåçîïàñíûå ìåòîäû (ò.å. PUT,
    POST, DELETE), èëè êîãäà îòâåò èìååò ïåðåíàïðàâëÿþùèé ñòàòóñ-êîä (íàïðèìåð, 301
    èëè 302).
    Symfony2

    àâòîìàòè÷åñêè
    óñòàíàâëèâàåò
    ðàçóìíî-êîíñåðâàòèâíûé
    çàãîëîâîê
    Cache-Control, åñëè ðàçðàáîò÷èê íå çàäàë ïðàâèëà êýøèðîâàíèÿ ÿâíî. Ýòè óìîë÷àíèÿ
    ñëåäóþò ñëåäóþùèì ïðàâèëàì:
    ˆ Åñëè íå îïðåäåëåíû çàãîëîâêè êýøèðîâàíèÿ (Cache-Control, Expires, ETag èëè
    Last-Modified), Cache-Control óñòàíàâëèâàåòñÿ â çíà÷åíèå no-cache, òî åñòü îòâåò êýøèðîâàòüñÿ íå áóäåò;
    ˆ Åñëè Cache-Control ïóñòîé (íî ïðèñóòñòâóåò ëþáîé äðóãîé êýøèðóþùèé çàãîëîâîê), åãî çíà÷åíèå óñòàíàâëèâàåòñÿ â private, must-revalidate;
    ˆ Åñëè ïðèñóòñòâóåò õîòÿ áû îäíà äèðåêòèâà Cache-Control è ÿâíî íå óêàçàíû
    äèðåêòèâû public èëè private, Symfony2 äîáàâëÿåò äèðåêòèâó private àâòîìàòè÷åñêè (çà èñêëþ÷åíèåì ñëó÷àÿ, êîãäà óñòàíîâëåí s-maxage).
    2.13.4 Ìîäåëè êýøèðîâàíèÿ â HTTP: expiration è validation

    Ñïåöèôèêàöèÿ HTTP îïðåäåëÿåò äâå ìîäåëè êýøèðîâàíèÿ:
    ˆ Ïåðâàÿ - ìîäåëü îêîí÷àíèÿ ñðîêà äåéñòâèÿ (expiration), âû ïðîñòî óêàçûâàåòå
    êàê äîëãî îòâåò áóäåò ñâåæèì, âêëþ÷àÿ çàãîëîâêè Cache-Control è/èëè Expires.
    Êýøåðû, êîòîðûå ïîääåðæèâàþò ýòó ìîäåëü, íå áóäóò âûïîëíÿòü íåêîòîðûé çàïðîñ äî òåõ ïîð, ïîêà åãî êýøèðîâàííàÿ âåðñèÿ íå äîñòèãíåò îêîí÷àíèÿ ñðîêà
    äåéñòâèÿ (expiration) è íå ñòàíåò ïðîñðî÷åííîé.
    ˆ Êîãäà ñòðàíèöû î÷åíü áûñòðî ìåíÿþòñÿ, ÷àñòî áûâàåò íåîáõîäèìî èñïîëüçîâàòü
    ìîäåëü âàëèäàöèè (validation). Ïðè èñïîëüçîâàíèè ýòîé ìîäåëè êýøåð ñîõðàíÿåò
    îòâåò, íî ïðè êàæäîì ïîñëåäóþùåì çàïðîñå îí çàïðàøèâàåò ñåðâåð - ÿâëÿåòñÿ
    ëè êýøèðîâàííûé îòâåò âàëèäíûì èëè íåò. Ïðèëîæåíèå èñïîëüçóåò íåêîòîðûé
    óíèêàëüíûé èäåíòèôèêàòîð îòâåòà (çàãîëîâîê Etag) è/èëè âðåìåííóþ ìåòêó (çàãîëîâîê Last-Modified) äëÿ ïðîâåðêè èçìåíèëàñü ëè ñòðàíèöà ñ ìîìåíòà å¼ êýøèðîâàíèÿ.
    Öåëüþ îáîèõ ýòèõ ìîäåëåé ÿâëÿåòñÿ ñëåäóþùàÿ: íå ãåíåðèðîâàòü îäèí è òîò æå îòâåò
    äâàæäû, åñëè â êýøå óæå åñòü ñâåæèé îòâåò, ñîõðàí¼ííûé òàì ðàíåå.

    254

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ×èòàåì ñïåöèôèêàöèþ HTTP
    Ñïåöèôèêàöèÿ HTTP îïðåäåëÿåò ïðîñòîé, íî ìîùíûé ÿçûê, ïðè ïîìîùè êîòîðîãî îñóùåñòâëÿþòñÿ êëèåíò-ñåðâåðíûå êîììóíèêàöèè â ñåòè. Ìîäåëü çàïðîñ-îòâåò
    îïðåäåëÿåò âñþ âàøó ðàáîòó, êàê âåá-ðàçðàáîò÷èêà. Ê íåñ÷àñòüþ, îðèãèíàëüíóþ
    ñïåöèôèêàöèþ - RFC 2616 - ÷èòàòü âåñüìà íåïðîñòî.
    Â íàñòîÿùåå âðåìÿ ñóùåñòâóåò èíèöèàòèâà (HTTP Bis) ïî ïåðåïèñûâàíèþ RFC
    2616. Îíà íå ñòàâèò öåëüþ íàïèñàíèå íîâîé âåðñèè HTTP, à â îñíîâíîì ñîñðåäîòî÷åíà íà ðàçúÿñíåíèè îðèãèíàëüíîé ñïåöèôèêàöèè HTTP. Ñòðóêòóðà ñïåöèôèêàöèè
    òàêæå ïîäâåðãëàñü óëó÷øåíèþ - îíà ðàçáèòà íà ñåìü ÷àñòåé; âñ¼ ÷òî îòíîñèòñÿ ê
    HTTP êýøèðîâàíèþ - ðàñïîëîæåíî â äâóõ íåçàâèñèìûõ ÷àñòÿõ (P4 - Conditional
    Requests è P6 - Caching: Browser and intermediary caches).
    Âàì, êàê âåá ðàçðàáîò÷èêó, ìû íàñòîÿòåëüíî ðåêîìåíäóåì ïðî÷èòàòü ýòó ñïåöèôèêàöèþ. ż ïðîñòîòà è ñèëà - äàæå ñïóñòÿ äåñÿòü ëåò ïîñëå å¼ íàïèñàíèÿ - áåñöåííû.
    È íå áîéòåñü âíåøíåãî âèäà ñïåöèôèêàöèè - å¼ ñîäåðæàíèå ìíîãî ëó÷øå, ÷åì å¼
    îáëîæêà.

    HTTP Expiration - îêîí÷àíèå ñòðîêà äåéñòâèÿ

    Ìîäåëü îêîí÷àíèÿ ñðîêà äåéñòâèÿ áîëåå ýôôåêòèâíàÿ è ïðîñòàÿ èç äâóõ ïîääåðæèâàåìûõ ìîäåëåé êýøèðîâàíèÿ è äîëæíà èñïîëüçîâàòüñÿ âåçäå, ãäå ýòî âîçìîæíî. Êîãäà
    îòâåò êýøèðóåòñÿ ñî ñðîêîì îêîí÷àíèÿ äåéñòâèÿ, êýø áóäåò õðàíèòü îòâåò è âîçâðàùàòü
    åãî íà êëèåíò íàïðÿìóþ, íå çàòðàãèâàÿ ïðèëîæåíèå, ïîêà ñðîê äåéñòâèÿ íå îêîí÷èòñÿ.
    Ìîäåëü îêîí÷àíèÿ ñðîêà äåéñòâèÿ ìîæåò áûòü çàäåéñòâîâàíà ñ èñïîëüçîâàíèåì äâóõ
    ïîõîæèõ HTTP çàãîëîâêîâ: Expires èëè Cache-Control.
    Îêîí÷àíèå ñðîêà äåéñòâèÿ ïðè ïîìîùè çàãîëîâêà

    Expires

    Ñëåäóÿ ñïåöèôèêàöèè HTTP, çàãîëîâîê Expires ñîäåðæèò äàòó/âðåìÿ, ïîñëå êîòîðîãî
    ýòîò îòâåò áóäåò ñ÷èòàòüñÿ ïðîñðî÷åííûì. Çàãîëîâîê Expires ìîæåò áûòü óñòàíîâëåí
    ïðè ïîìîùè ìåòîäà setExpires() êëàññà Response. Îí ïðèíèìàåò ýêçåìïëÿð DateTime
    â êà÷åñòâå àðãóìåíòà:
    //...
    $date = new DateTime();
    $date->modify('+600 seconds');
    $response->setExpires($date);

    Ðåçóëüòèðóþùèé çàãîëîâîê áóäåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:

    2.13.

    HTTP Êýøèðîâàíèå

    255

    Symfony Documentation, Âûïóñê 2.0

    Expires: Thu, 01 Mar 2011 16:00:00 GMT

    Ïðèìå÷àíèå: Ìåòîä setExpires() àâòîìàòè÷åñêè êîíâåðòèðóåò äàòó â çîíó GMT,
    êàê òîãî òðåáóåò ñïåöèôèêàöèÿ.

    Çàãîëîâîê Expires èìååò 2 îãðàíè÷åíèÿ. Ïåðâîå, ÷àñû íà âåá-ñåðâåðå è è ÷àñû êýøåðà (íàïðèìåð, áðàóçåðà) äîëæíû áûòü ñèíõðîíèçèðîâàííûìè. Âòîðîå, ñëåäóåò èç ñïåöèôèêàöèè è ãëàñèò, ÷òî HTTP/1.1 ñåðâåðû íèêîãäà íå äîëæíû óñòàíàâëèâàòü äàòó
    Expires áîëåå ÷åì íà îäèí ãîä âïåð¼ä.
    Îêîí÷àíèå ñðîêà äåéñòâèÿ ïðè ïîìîùè çàãîëîâêà

    Cache-Control

    Ïîñêîëüêó çàãîëîâîê Expires èìååò îãðàíè÷åíèÿ, âû äîëæíû èñïîëüçîâàòü çàãîëîâîê
    Cache-Control. Âñïîìèíàéòå, ÷òî çàãîëîâîê Cache-Control èñïîëüçóåòñÿ äëÿ óêàçàíèÿ
    ðàçëè÷íûõ äèðåêòèâ, îòíîñÿùèõñÿ ê êýøèðîâàíèþ. Äëÿ îêîí÷àíèÿ ñðîêà äåéñòâèÿ èìåþòñÿ äâå äèðåêòèâû, max-age è s-maxage. Ïåðâàÿ èñïîëüçóåòñÿ âñåìè êýøåðàìè, â òî
    âðåìÿ êàê âòîðàÿ èñïîëüçóåòñÿ ëèøü îáùèìè (shared) êýøàìè:
    //...
    // Óñòàíàâëèâàåì ÷èñëî ñåêóíä, ïîñëå êîòîðîãî îòâåò áîëåå íå áóäåò ñ÷èòàòüñÿ ñâåæèì
    $response->setMaxAge(600);
    // Òîæå ÷òî è âûøå, íî òîëüêî äëÿ îáùèõ êýøåé
    $response->setSharedMaxAge(600);

    Çàãîëîâîê Cache-Control áóäåò èìåòü ñëåäóþùèé ôîðìàò (òàêæå òàì ìîãóò áûòü è
    äðóãèå äèðåêòèâû):
    Cache-Control: max-age=600, s-maxage=600

    Âàëèäàöèÿ

    Êîãäà íåêîòîðûé ðåñóðñ äîëæåí áûòü îáíîâë¼í, â ñâÿçè ñ òåì, ÷òî ïðîèçîøëè èçìåíåíèÿ â äàííûõ, ëåæàùèõ â åãî îñíîâå, ìîäåëü îêîí÷àíèÿ ñðîêà äåéñòâèÿ ñòàíîâèòñÿ
    íåñîñòîÿòåëüíîé. Ïðè ïîäõîäå, èñïîëüçóåìîì â ìîäåëè îêîí÷àíèÿ ñðîêà äåéñòâèÿ, êýø
    íå îáðàòèòñÿ ê ïðèëîæåíèþ äëÿ îáíîâëåíèÿ îòâåòà ïîêà äàííûå íå ñòàíîâÿòñÿ ïðîñðî÷åííûìè (ò.å. êîãäà èñòå÷¼ò ñðîê äåéñòâèÿ êýøèðîâàííîãî îòâåòà).
    Ìîäåëü âàëèäàöèè ðåøàåò ýòó ïðîáëåìó. Ñ å¼ ïîìîùüþ êýø òàêæå ïðîäîëæàåò ñîõðàíÿòü îòâåòû. Ðàçëè÷èå çàêëþ÷àåòñÿ â òîì, ÷òî äëÿ êàæäîãî çàïðîñà, êýø çàïðàøèâàåò
    ïðèëîæåíèå èçìåíèëñÿ èëè íåò çàïðàøèâàåìûé ðåñóðñ. Åñëè êýø åù¼ âàëèäåí, âàøå
    ïðèëîæåíèå äîëæíî âåðíóòü ñòàòóñ êîä 304 è íå âîçâðàùàòü êîíòåíò. Ýòî îçíà÷àåò, ÷òî
    êýø åù¼ âàëèäåí è ìîæíî âîçâðàùàòü êýøèðîâàííûé îòâåò.
    256

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ñ ýòîé ìîäåëüþ âû, ïðåæäå âñåãî, ñîõðàíÿåòå ïðîïóñêíóþ ñïîñîáíîñòü âàøåãî èíòåðíåòêàíàëà, òàê êàê ñòðàíèöà öåëèêîì íå îòñûëàåòñÿ äâàæäû òîìó æå êëèåíòó (âìåñòî ýòîãî
    áóäåò îòïðàâëåí îòâåò ñî ñòàòóñ êîäîì 304). Íî, åñëè âû àêêóðàòíî ïðîåêòèðóåòå âàøå
    ïðèëîæåíèå, ìû ìîæåòå ïîëó÷èòü íåîáõîäèìûé ìèíèìóì äàííûõ, íåîáõîäèìûõ äëÿ òîãî ÷òîáû îòïðàâèòü ñòàòóñ êîä 304 è ñîõðàíèòü òàêæå ðåñóðñû CPU è/èëè îïåðàòèâíîé
    ïàìÿòè (ñì. íèæå ðåàëèçàöèþ ýòîãî âàðèàíòà).

    Ñîâåò: Ñòàòóñ 304 îçíà÷àåò Not Modied. Ýòî âàæíûé ñòàòóñ, òàê êàê âìåñòå ñ íèì íå

    îòïðàâëÿåòñÿ çàïðîøåííûé êîíòåíò. Âìåñòî ýòîãî, îòâåò ñîñòîèò èç íåáîëüøîãî íàáîðà
    óêàçàíèé, êîòîðûå ñîîáùàþò êýøó, ÷òî ìîæíî èñïîëüçîâàòü ñîõðàí¼ííóþ ðàíåå âåðñèþ.
    Êàê è â ñëó÷àå ñ ìîäåëüþ îêîí÷àíèÿ ñðîêà äåéñòâèÿ, åñòü äâà HTTP çàãîëîâêà, êîòîðûå
    ìîãóò áûòü èñïîëüçîâàíû äëÿ ðåàëèçàöèè ìîäåëè âàëèäàöèè: ETag è Last-Modified.
    Âàëèäàöèÿ ïðè ïîìîùè çàãîëîâêà

    ETag

    Çàãîëîâîê ETag - ýòî ñòðîêîâûé çàãîëîâîê (íàçûâàåìûé entity-tag), êîòîðûé åäèíñòâåííûì îáðàçîì èäåíòèôèöèðóåò ïðåäñòàâëåíèå öåëåâîãî ðåñóðñà. Îí ãåíåðèðóåòñÿ
    è óñòàíàâëèâàåòñÿ âñåöåëî âíóòðè âàøåãî ïðèëîæåíèÿ, òàê ÷òî âû ìîæåòå ïîíÿòü, ê
    ïðèìåðó, ñîîòâåòñòâóåò ëè êýøèðîâàííûé ðåñóðñ /about òîìó, êîòîðûé âàøå ïðèëîæåíèå ñîáèðàåòñÿ âåðíóòü. Çàãîëîâîê ETag ïîõîæ íà îòïå÷àòêè ïàëüöåâ è èñïîëüçóåòñÿ
    äëÿ áûñòðîãî îïðåäåëåíèÿ ýêâèâàëåíòíû ëè äâå âåðñèè ðåñóðñà. Êàê è îòïå÷àòêè ïàëüöåâ, êàæäûé ETag äîëæåí áûòü óíèêàëüíûì äëÿ ëþáîãî ïðåäñòàâëåíèÿ îäíîãî è òîãî
    æå ðåñóðñà.
    Äàâàéòå âçãëÿíåì íà ïðîñòóþ ðåàëèçàöèþ, êîòîðàÿ ãåíåðèðóåò ETag â âèäå md5 õýøà
    îò êîíòåíòà:
    //...
    public function indexAction()
    {
    $response = $this->render('MyBundle:Main:index.html.twig');
    $response->setETag(md5($response->getContent()));
    $response->isNotModified($this->getRequest());
    }

    return $response;

    Ìåòîä Response::isNotModified() ñðàâíèâàåò ETag, îòïðàâëåííûé â çàïðîñå (Request)
    ñ ýòèì æå òàãîì â îòâåòå (Response). Åñëè îíè ñîâïàäàþò, ýòîò ìåòîä àâòîìàòè÷åñêè
    óñòàíàâëèâàåò äëÿ Response ñòàòóñ êîä 304.
    Ýòîò àëãîðèòì äîñòàòî÷íî ïðîñòîé è âïîëíå òèïè÷íûé, íî âàì íóæíî ñîçäàòü ýêçåìïëÿð Response öåëèêîì, ïåðåä òåì êàê âû ïîëó÷èòå âîçìîæíîñòü ñðàâíèòü ETag'è, à

    2.13.

    HTTP Êýøèðîâàíèå

    257

    Symfony Documentation, Âûïóñê 2.0

    ýòî âåñüìà ðàñòî÷èòåëüíî. Äðóãèìè ñëîâàìè, ýòîò ïîäõîä ñîõðàíÿåò ïðîïóñêíóþ ñïîñîáíîñòü, íî íå ðåñóðñû CPU.
    Â ñåêöèè Îïòèìèçàöèÿ âàøåãî êîäà ïðè ïîìîùè ìåòîäà âàëèäàöèè ìû ïîêàæåì êàê
    ìîæíî èñïîëüçîâàòü âàëèäàöèþ áîëåå èíòåëëèãåíòíî è îïðåäåëÿòü âàëèäíîñòü êýøà
    áåç èçëèøíèõ çàòðàò ðåñóðñîâ ñåðâåðà.

    Ñîâåò:

    äëÿ ýòîâ
    ìåòîä
    :method:`Symfony\\Component\\HttpFoundation\\Response::setETag`.

    ãî

    íàäî

    Symfony2
    ïåðåäàòü

    òàêæå

    true

    ïîääåðæèâàåò
    â
    êà÷åñòâå

    Âàëèäàöèÿ ïðè ïîìîùè çàãîëîâêà

    ñëàáûå
    âòîðîãî

    ETag'è àðãóìåíòà

    Last-Modified

    Çàãîëîâîê Last-Modified - ýòî âòîðîé âîçìîæíûé ñïîñîá âàëèäàöèè. Ñëåäóÿ ñïåöèôèêàöèè HTTP, Çàãîëîâîê Last-Modified ñîäåðæèò äàòó è âðåìÿ, êîãäà ïðåäñòàâëåíèå
    ðåñóðñà áûëî èçìåíåíî â ïîñëåäíèé ðàç, ïî âåðñèè èñõîäíîãî ñåðâåðà. Äðóãèìè ñëîâàìè, ïðèëîæåíèå ïðèíèìàåò ðåøåíèå î òîì, äîëæåí ëè áûòü îáíîâë¼í êýøèðîâàííûé
    êîíòåíò, îñíîâûâàÿñü íà òîì, èçìåíÿëñÿ ëè îí ñî âðåìåíè êýøèðîâàíèÿ.
    Íàïðèìåð, âû ìîæåòå èñïîëüçîâàòü äàòó ïîñëåäíåãî îáíîâëåíèÿ äëÿ âñåõ îáúåêòîâ, íåîáõîäèìûõ äëÿ ñîçäàíèÿ ïðåäñòàâëåíèÿ ðåñóðñà â êà÷åñòâå çíà÷åíèÿ çàãîëîâêà
    Last-Modified:
    //...
    public function showAction($articleSlug)
    {
    // ...
    $articleDate = new \DateTime($article->getUpdatedAt());
    $authorDate = new \DateTime($author->getUpdatedAt());
    $date = $authorDate > $articleDate ? $authorDate : $articleDate;
    $response->setLastModified($date);
    $response->isNotModified($this->getRequest());
    }

    return $response;

    Ìåòîä Response::isNotModified() ñðàâíèâàåò çàãîëîâîê If-Modified-Since, îòïðàâëåííûé â çàïðîñå ñ çàãîëîâêîì Last-Modified, óñòàíîâëåííîì â îòâåòå. Åñëè îíè èäåíòè÷íû, Response áóäåò óñòàíîâëåí ñòàòóñ êîä 304.

    Ïðèìå÷àíèå:
    258

    Çàãîëîâîê çàïðîñà If-Modified-Since ñîîòâåòñòâóåò çàãîëîâêó
    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Last-Modified ïîñëåäíåãî îòâåòà, îòïðàâëåííîãî êëèåíòó äëÿ íåêîòîðîãî ðåñóðñà. Òàêèì îáðàçîì, êëèåíò è ñåðâåð îáùàþòñÿ äðóã ñ äðóãîì è îïðåäåëÿþò áûë ëè ðåñóðñ
    îáíîâë¼í ñ ìîìåíòà åãî êýøèðîâàíèÿ.

    Îïòèìèçàöèÿ âàøåãî êîäà ïðè ïîìîùè ìåòîäà âàëèäàöèè

    Îñíîâíàÿ öåëü ëþáîé ñòðàòåãèè êýøèðîâàíèÿ - ïîíèçèòü íàãðóçêó íà ïðèëîæåíèå. Èíûìè ñëîâàìè, ÷åì ìåíüøå äåëàåò âàøå ïðèëîæåíèå äëÿ òîãî, ÷òîáû âåðíóòü îòâåò 304,
    òåì ëó÷øå. Ìåòîä Response::isNotModified() èìåííî ýòèì è çàíèìàåòñÿ ïðè èñïîëüçîâàíèè ïðîñòîãî è ýôôåêòèâíîãî øàáëîíà:
    //...
    public function showAction($articleSlug)
    {
    // Ïîëó÷àåì ìèíèìóì èíôîðìàöèè äëÿ âû÷èñëåíèÿ
    // çíà÷åíèé äëÿ çàãîëîâêîâ ETag èëè Last-Modified
    // (îñíîâûâàÿñü íà çàïðîñå Request, äàííûõ, ïîëó÷àåìûõ èç áàçû äàííûõ
    // èëè æå èç õðàíèëèùà êëþ÷-çíà÷åíèå)
    $article = // ...
    // Ñîçäà¼ì îòâåò Response ñ çàãîëîâêîì ETag è/èëè Last-Modified
    $response = new Response();
    $response->setETag($article->computeETag());
    $response->setLastModified($article->getPublishedAt());
    // Ïðîâåðÿåì, ÷òî îòâåò íå ìîäèôèöèðîâàëñÿ äëÿ ýòîãî çàïðîñà
    if ($response->isNotModified($this->getRequest())) {
    // âîçâðàùàåì îòâåò 304
    return $response;
    } else {
    // äåëàåì äîïîëíèòåëüíûå äåéñòâèÿ, íàïðèìåð, ïîëó÷àåì äîïîëíèòåëüíûå äàííûå
    $comments = // ...

    }

    }

    // èëè îòîáðàæàåì øàáëîí ïðè ïîìîùè $response, êîòîðûé áûë ñîçäàí ðàíåå
    return $this->render(
    'MyBundle:MyController:article.html.twig',
    array('article' => $article, 'comments' => $comments),
    $response
    );

    Åñëè îòâåò Response íå ìîäèôèöèðîâàëñÿ, ìåòîä isNotModified() àâòîìàòè÷åñêè óñòàíàâëèâàåò ñòàòóñ êîä îòâåòà â 304, óäàëÿåò êîíòåíò è óäàëÿåò íåêîòîðûå çàãîëîâêè, êîòîðûå íå äîëæíû ïðèñóòñòâîâàòü â îòâåòå 304 (ñì. ìåòîä
    2.13.

    HTTP Êýøèðîâàíèå

    259

    Symfony Documentation, Âûïóñê 2.0

    :method:`Symfony\\Component\\HttpFoundation\\Response::setNotModied`).
    Âàðèàöèè îòâåòà

    Ðàíåå âû óçíàëè, ÷òî êàæäûé URI èìååò åäèíñòâåííîå ïðåäñòàâëåíèå öåëåâîãî ðåñóðñà. Ïî óìîë÷àíèþ, HTTP êýøèðîâàíèå âûïîëíÿåòñÿ ñ èñïîëüçîâàíèåì URI ðåñóðñà â
    êà÷åñòâå êëþ÷à ê çíà÷åíèþ êýøà. Åñëè äâà ÷åëîâåêà çàïðîñÿò îäèí è òîò æå URI äëÿ
    êýøèðóåìîãî ðåñóðñà, âòîðîé êëèåíò ïîëó÷èò óæå êýøèðîâàííóþ âåðñèþ.
    Èíîãäà ýòîãî íå äîñòàòî÷íî è òðåáóåòñÿ êýøèðîâàòü ðàçëè÷íûå âåðñèè îäíîãî è òîãî
    æå URI, îñíîâûâàÿñü íà çíà÷åíèè îäíîãî èëè íåñêîëüêèõ çàãîëîâêîâ. Íàïðèìåð, åñëè âû ñæèìàåòå ñòðàíèöû äëÿ êëèíåíòîâ, êîòîðûå ïîääåðæèâàþò ñæàòèå, ëþáîé URI
    áóäåò èìåòü äâà ïðåäñòàâëåíèÿ: îäíî äëÿ êëèåíòîâ, ïîääåðæèâàþùèõ ñæàòèå, è îäíî
    äëÿ òåõ êòî íå ïîääåðæèâàåò. Ýòî îïðåäåëÿåòñÿ íà îñíîâå çíà÷åíèÿ çàãîëîâêà çàïðîñà
    Accept-Encoding.
     ýòîì ñëó÷àå, âàì íåîáõîäèìî õðàíèòü îáå âåðñèè îòâåòà äëÿ íåêîòîðîãî ðåñóðñà ñæàòóþ è íå ñæàòóþ è âîçâðàùàòü åå, îñíîâûâàÿñü íà çíà÷åíèè çàãîëîâêà çàïðîñà
    Accept-Encoding. Ýòîãî ìîæíî äîñòè÷ü ïðè ïîìîùè çàãîëîâêà îòâåòà Vary, êîòîðûé
    ÿâëÿåòñÿ ñïèñêîì (ðàçäåëèòåëè - çàïÿòûå) ðàçëè÷íûõ çàãîëîâêîâ, ÷üè çíà÷åíèÿ ïåðåêëþ÷àþò ðàçëè÷íûå ïðåäñòàâëåíèÿ çàïðîøåííîãî ðåñóðñà:
    Vary: Accept-Encoding, User-Agent

    Ñîâåò: Çàãîëîâîê Vary èç ïðèìåðà âûøå ïîçâîëÿåò êýøèðîâàòü ðàçëè÷íûå âåðñèè äëÿ
    êàæäîãî ðåñóðñà, îñíîâûâàÿñü íà URI è çíà÷åíèè çàãîëîâêîâ çàïðîñà Accept-Encoding
    è User-Agent.
    Îáúåêò Response ïðåäîñòàâëÿåò ïðîñòîé èíòåðôåéñ äëÿ óïðàâëåíèÿ çàãîëîâêîì Vary:
    //...
    // óñòàíàâëèâàåì îäèí çàãîëîâîê vary
    $response->setVary('Accept-Encoding');
    // óñòàíàâëèâàåì íåñêîëüêî çàãîëîâêîâ vary
    $response->setVary(array('Accept-Encoding', 'User-Agent'));

    Ìåòîä setVary() ïðèíèìàåò â êà÷åñòâå ïàðàìåòðà èìÿ çàãîëîâêà èëè æå ìàññèâ íàèìåíîâàíèé çàãîëîâêîâ, íà îñíîâàíèè çíà÷åíèé êîòîðûõ íåîáõîäèìî âàðüèðîâàòü îòâåò.
    Îêîí÷àíèå ñðîêà äåéñòâèÿ è âàëèäàöèÿ

    Âû ìîæåòå èñïîëüçîâàòü îêîí÷àíèå ñðîêà äåéñòâèÿ ñîâìåñòíî ñ âàëèäàöèåé â îäíîì è
    òîì æå ýêçåìïëÿðå Response. Åñëè îêîí÷àíèå ñðîêà äåéñòâèÿ ðàáîòàåò ðàíüøå âàëèäà260

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    öèè, âû ñìîæåòå ïîëó÷èòü ëó÷øèå ïðåèìóùåñòâà îò îáåèõ ìîäåëåé. Äðóãèìè ñëîâàìè,
    èñïîëüçóÿ ñîâìåñòíî ìîäåëè îêîí÷àíèå ñðîêà äåéñòâèÿ è âàëèäàöèè âû ìîæåòå ïðîèíñòðóêòèðîâàòü êýø õðàíèòü êîíòåíò ïîêà ñ íåêîòîðûì èíòåðâàëîì îñóùåñòâëÿåòñÿ
    (îêîí÷àíèå ñðîêà äåéñòâèÿ) ïðîâåðêà, ÷òî êîíòåíò âñ¼ åù¼ âàëèäåí.
    Äðóãèå ìåòîäû êëàññà Response

    Êëàññ Response ñîäåðæèò òàêæå äðóãèå ìåòîäû äëÿ ðàáîòû ñ êýøåì. Ïðèìåð íèæå
    èëëþñòðèðóåò ñàìûå ÷àñòî óïîòðåáëÿåìûå èç íèõ:
    // ïîìåòèòü îòâåò êàê "ïðîñðî÷åííûé"
    $response->expire();
    // Ôîðñèðîâàòü âîçâðàò îòâåòà 304 áåç êîíòåíòà
    $response->setNotModified();

    Â äîïîëíåíèå ê ýòîìó, âñå îñíîâíûå HTTP îòíîñÿùèåñÿ ê êýøó, ìîãóò áûòü óñòàíîâëåíû
    ïðè ïîìîùè îäíîãî ìåòîäà setCache():
    // Óñòàíîâèòü çàãîëîâêè äëÿ êýøèðîâàíèÿ îäíèì âûçîâîì
    $response->setCache(array(
    'etag'
    => $etag,
    'last_modified' => $date,
    'max_age'
    => 10,
    's_maxage'
    => 10,
    'public'
    => true,
    // 'private'
    => true,
    ));

    2.13.5 Èñïîëüçîâàíèå ESI (Edge Side Includes)

    Êýøèðóþùèå øëþçû - ýòî îòëè÷íûé ñïîñîá ñäåëàòü âàø ñàéò áîëåå ïðîèçâîäèòåëüíûì.
    Íî îíè òàêæå èìåþò è îäíî îãðàíè÷åíèå: îíè ìîãóò êýøèðîâàòü ëèøü ñòðàíèöû öåëèêîì. Åñëè âû ïî êàêèì-òî ïðè÷èíàì íå ìîæåòå êýøèðîâàòü ñòðàíèöû öåëèêîì èëè â
    ñëó÷àå êîãäà ñòðàíèöà èìååò íåñêîëüêî äèíàìè÷åñêèõ ÷àñòåé, âû âûøëè èç çîíû óäà÷è.
    Ê ñ÷àñòüþ, Symfony2 ïðåäîñòàâëÿåò ðåøåíèå äëÿ ýòèõ ñëó÷àåâ, îñíîâàííîå íà òåõíîëîãèè ESI, èëè Edge Side Includes. Êîìïàíèÿ Akama ñîçäàëà ýòó ñïåöèôèêàöèþ ïî÷òè 10
    ëåò íàçàä, è îíà ïîçâîëÿåò èìåòü äëÿ îòäåëüíûõ ÷àñòåé ñòðàíèöû ðàçëè÷íûå ñòðàòåãèè
    êýøèðîâàíèÿ.
    Ñïåöèôèêàöèÿ ESI îïèñûâàåò òàãè, êîòîðûå âû ìîæåòå äîáàâèòü â âàøè ñòðàíèöû äëÿ
    îáùåíèÿ ñ êýøèðóþùèì øëþçîì. Â Symfony2 ðåàëèçîâàí ëèøü îäèí òàã - include, òàê
    êàê ýòî íàèáîëåå ïîëåçíûé òàã âíå êîíòåêñòà Akama:
    2.13.

    HTTP Êýøèðîâàíèå

    261

    Symfony Documentation, Âûïóñê 2.0



    Some content


    More content



    Ïðèìå÷àíèå: Îáðàòèòå âíèìàíèå, â ïðèìåðå âûøå, ÷òî äëÿ ESI òàãà óêàçàí ïîëíûé

    URL. ESI òàã ïðåäñòàâëÿåò ñîáîé ôðàãìåíò ñòðàíèöû, êîòîðûé ìîæíî ïîëó÷èòü ïî
    ýòîìó URL.
    Ïðè îáðàáîòêå çàïðîñà, êýøèðóþùèé øëþç ïîëó÷àåò ñòðàíèöó öåëèêîì èç ñâîåãî êýøà
    èëè æå çàïðàøèâàåò åãî ó ïðèëîæåíèÿ. Åñëè îòâåò ñîäåðæèò îäèí èëè áîëåå ESI òàãîâ,
    îíè îáðàáàòûâàþòñÿ òåì æå îáðàçîì. Äðóãèìè ñëîâàìè, êýøèðóùèé øëþç ïîëó÷àåò
    âêëþ÷¼ííûå ôðàãìåíòû ñòðàíèö èç ñâîåãî êýøà, ëèáî çàïðàøèâàåò ýòè ôðàãìåíòû ó
    ïðèëîæåíèÿ. Êîãäà âñå ESI òàãè îáðàáîòàíû, øëþç âêëþ÷àåò âñå ôðàãìåíòû â îñíîâíóþ
    ñòðàíèöó è îòïðàâëÿåò èòîãîâûé êîíòåíò êëèåíòó.
    Âñ¼ ýòî ïðîèñõîäèò íåçàìåòíî íà óðîâíå êýøèðóþùåãî øëþçà (ò.å. âíå âàøåãî ïðèëîæåíèÿ). Êàê âû óâèäèòå äàëåå, åñëè âû çàõîòèòå èñïîëüçîâàòü ïðåèìóùåñòâà, êîòîðûå
    ïðåäîñòàâëÿþò ESI òàãè, Symfony2 ïîçâîëèò âàì ïîäêëþ÷àòü èõ íå ïðèëàãàÿ îñîáûõ
    óñèëèé.
    Èñïîëüçîâàíèå ESI â Symfony2

    Âî-ïåðâûõ, ïåðåä èñïîëüçîâàíèåì ESI, óáåäèòåñü, ÷òî âû àêòèâèðîâàëè èõ â íàñòðîéêàõ
    ïðèëîæåíèÿ:
    ˆ

    YAML

    # app/config/config.yml
    framework:
    # ...
    esi: { enabled: true }

    ˆ

    XML






    262

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    // ...
    'esi'
    => array('enabled' => true),
    ));

    Òåïåðü, ïðåäïîëîæèì, ÷òî ó âàñ åñòü ñòðàíèöà, êîòîðàÿ ïî áîëüøåé ÷àñòè ñòàòè÷åñêàÿ,
    çà èñêëþ÷åíèåì íîâîñòåé, ðàñïîëîæåííûõ ïîä êîíòåíòîì. Ïðè ïîìîùè ESI âû ìîæåòå
    êýøèðîâàòü íîâîñòè íåçàâèñèìî îò îñòàëüíîé ñòðàíèöû.
    // ...
    public function indexAction()
    {
    $response = $this->render('MyBundle:MyController:index.html.twig');
    $response->setSharedMaxAge(600);
    }

    return $response;

    Â ýòîì ïðèìåðå âû óñòàíàâëèâàåòå äëÿ âñåé ñòðàíèöû âðåìÿ æèçíè êýøà â 10 ìèíóò.
    Çàòåì, ïîäêëþ÷èòå íîâîñòè â øàáëîí ïðè ïîìîùè âñòðàèâàíèÿ äåéñòâèÿ. Ýòî ìîæíî
    ñäåëàòü ïðè ïîìîùè õåëïåðà render (ñì. Âíåäðåíèå êîíòðîëëåðîâ ).
    Òàê êàê âñòðîåííûé êîíòåíò ïîñòóïàåò èç äðóãîé ñòðàíèöû (èëè êîíòðîëëåðà â äàííîì
    ñëó÷àå), Symfony2 èñïîëüçóåò ñòàíäàðòíûé õýëïåð render äëÿ êîíôèãóðèðîâàíèÿ ESI
    òàãà:
    ˆ

    Twig

    {% render '...:news' with {}, {'standalone': true} %}

    ˆ

    PHP

    render('...:news', array(), array('standalone' => true)) ?>

    Óêàçàâ ïàðàìåòð standalone ðàâíûé true, âû ãîâîðèòå Symfony2, ÷òî äåéñòâèå äîëæíî
    îòîáðàæàòüñÿ êàê ESI òàã. Âû âîçìîæíî óäèâëåíû - çà÷åì èñïîëüçîâàòü õåëïåð, âìåñòî
    òîãî, ÷òîáû íàïèñàòü ESI òàã ñàìîñòîÿòåëüíî. Ýòî íåîáõîäèìî äëÿ òîãî, ÷òîáû âàøå
    ïðèëîæåíèå ðàáîòàëî äàæå åñëè íå óñòàíîâëåí íèêàêîé êýøèðóþùèé øëþç. Äàâàéòå
    ðàçáåð¼ì, êàê ðàáîòàåò ýòà êîíñòðóêöèÿ.
    Êîãäà îïöèÿ standalone èìååò çíà÷åíèå false (ïî óìîë÷àíèþ), Symfony2 îáúåäèíÿåò êîíòåíò ïîäêëþ÷¼ííîé ñòðàíèöû ñ êîíòåíòîì îñíîâíîé ïåðåä îòïðàâêîé îòâåòà íà

    2.13.

    HTTP Êýøèðîâàíèå

    263

    Symfony Documentation, Âûïóñê 2.0

    êëèåíò. Íî êîãäà standalone èìååò çíà÷åíèå true, è åñëè Symfony2 îïðåäåëÿåò, ÷òî êýøèðóþùèé øëþç, ÷åðåç êîòîðûé ðàáîòàåò ïðèëîæåíèå, ïîääåðæèâàåò ESI, ãåíåðèòñÿ
    ESI òàã. Íî åñëè øëþç íå îáíàðóæåí èëè æå îí íå ïîääåðæèâàåò ESI, Symfony2 áóäåò
    îáúåäèíÿòü êîíòåíò ïîäêëþ÷¼ííîé ñòðàíèöû ñ êîíòåíòîì îñíîâíîé òàêæå, êàê ýòî áûëî
    áû âûïîëíåíî ïðè çíà÷åíèè standalone ðàâíîì false.

    Ïðèìå÷àíèå: Symfony2 îïðåäåëÿåò, ïîääåðæèâàåò ëè øëþç ESI, ïðè ïîìîùè äðóãîé

    ñïåöèôèêàöèè Akama, êîòîðàÿ ïîääåðæèâàåòñÿ îáðàòíûì ïðîêñè Symfony2 èç êîðîáêè.
    Òåïåðü äëÿ âñòðîåííîãî äåéñòâèÿ âû ìîæåòå óêàçàòü ñîáñòâåííûå ïðàâèëà êýøèðîâàíèÿ, íåçàâèñèìî îò ãëàâíîé ñòðàíèöû:
    public function newsAction()
    {
    // ...
    }

    $response->setSharedMaxAge(60);

    Ïðè ïîìîùè ESI êýø ñòðàíèöû áóäåò âàëèäíûì â òå÷åíèå 600 ñåêóíä, íî êîìïîíåíò
    íîâîñòåé áóäåò êýøèðîâàòüñÿ òîëüêî íà 60 ñåêóíä.
    Òðåáîâàíèåì, ïðè èñïîëüçîâàíèè ESI, ÿâëÿåòñÿ ñëåäóþùåå: âñòðîåííîå äåéñòâèå äîëæíî áûòü äîñòóïíî ÷åðåç íåêîòîðûé URL, ÷òîáû êýøèðóþùèé øëþç ìîã ïîëó÷èòü åãî
    êîíòåíò íåçàâèñèìî îò îñòàëüíîé ñòðàíèöû. Êîíå÷íî, äåéñòâèå íå ìîæåò áûòü äîñòóïíûì áåç ìàðøðóòà, êîòîðûé óêàçûâàåò íà íåãî. Symfony2 çàáîòèòñÿ è îá ýòîì ïðè ïîìîùè áàçîâîãî ìàðøðóòà è êîíòðîëëåðà. ×òîáû ESI òàã include ðàáîòàë, âû äîëæíû
    îïðåäåëèòü ìàðøðóò _internal:
    ˆ

    YAML

    # app/config/routing.yml
    _internal:
    resource: "@FrameworkBundle/Resources/config/routing/internal.xml"
    prefix: /_internal

    ˆ

    XML




    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

    264

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0




    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    'translator' => array('fallback' => 'en'),
    ));

    Îïöèÿ fallback îïðåäåëÿåò ëîêàëü äëÿ îòêàòà, êîãäà ïåðåâîä íå ñóùåñòâóåò äëÿ ëîêàëè
    ïîëüçîâàòåëÿ.

    Ñîâåò: Êîãäà ïåðåâîä äëÿ ëîêàëè íå ñóùåñòâóåò, ïåðåâîä÷èê ïûòàåòñÿ ñíà÷àëà íàéòè

    ïåðåâîä äëÿ ÿçûêà (fr åñëè ëîêàëü fr_FR, íàïðèìåð). Åñëè ýòî òàêæå íå óäà¼òñÿ, îí
    èùåò ïåðåâîä, èñïîëüçóÿ ëîêàëü îòêàòà.
    Ëîêàëü èñïîëüçóåìàÿ ïðè ïåðåâîäå õðàíèòñÿ â ñåññèè ïîëüçîâàòåëÿ.
    2.14.2 Îñíîâû ïåðåâîäîâ

    Ïåðåâîä
    òåêñòà
    îñóùåñòâëÿåòñÿ
    ñåðâèñîì
    translator
    (Symfony\Component\Translation\Translator).
    Äëÿ
    ïåðåâîäà
    òåêñòîâîãî
    áëîêà
    (íàçûâàåìîãî
    ñîîáùåíèåì)
    èñïîëüçóéòå
    ìåòîä
    268

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    :method:`Symfony\\Component\\Translation\\Translator::trans`.

    æèì, íàïðèìåð, ÷òî âû ïåðåâîäèòå ïðîñòîå ñîîáùåíèå âíóòðè êîíòðîëëåðà:

    Ïðåäïîëî-

    // ...
    public function indexAction()
    {
    $t = $this->get('translator')->trans('Symfony2 is great');
    }

    return new Response($t);

    Ïðè âûïîëíåíèè ýòîãî êîäà, Symfony2 ïîïûòàåòñÿ ïåðåâåñòè ñîîáùåíèå Symfony2 is
    great, îñíîâûâàÿñü íà ëîêàëè ïîëüçîâàòåëÿ. Äëÿ ýòîãî íåîáõîäèìî óêàçàòü Symfony2
    êàê íåîáõîäèìî ïåðåâåñòè ýòî ñîîáùåíèå ïðè ïîìîùè ðåñóðñà äëÿ ïåðåâîäà, êîòîðûé
    ïðåäñòàâëÿåò ñîáîé íàáîð ïåðåâåä¼ííûõ ñîîáùåíèé äëÿ íóæíîé ëîêàëè. Ýòîò ñëîâàðü
    ïåðåâîäîâ ìîæåò áûòü ñîçäàí â íåñêîëüêèõ ðàçëè÷íûõ ôîðìàòàõ, ðåêîìåíäóåìûì æå
    ÿâëÿåòñÿ XLIFF ôîðìàò:
    ˆ

    XML







    Symfony2 is great
    J'aime Symfony2





    ˆ

    PHP

    // messages.fr.php
    return array(
    'Symfony2 is great' => 'J\'aime Symfony2',
    );

    ˆ

    YAML

    # messages.fr.yml
    Symfony2 is great: J'aime Symfony2

    Òåïåðü, åñëè ëîêàëüþ ïîëüçîâàòåëÿ áóäåò Ôðàíöóçñêàÿ (íàïðèìåð, fr_FR èëè fr_BE),
    ýòî ñîîáùåíèå áóäåò ïåðåâåäåíî êàê J'aime Symfony2.
    2.14.

    Ïåðåâîäû

    269

    Symfony Documentation, Âûïóñê 2.0

    Ïðîöåññ ïåðåâîäà

    Äëÿ òîãî ÷òîáû ïåðåâåñòè ñîîáùåíèå, Symfony2 èñïîëüçóåò ïðîñòîé ïðîöåññ:
    ˆ Îïðåäåëÿåòñÿ ëîêàëü òåêóùåãî ïîëüçîâàòåëÿ, êîòîðàÿ õðàíèòñÿ â ñåññèè;
    ˆ Çàãðóæàåòñÿ êàòàëîã ïåðåâîäîâ ñîîáùåíèé èç ñîîòâåòñòâóþùåãî ðåñóðñà, îïðåäåëÿåìîãî ëîêàëüþ (íàïðèìåð, fr_FR), ñîîáùåíèÿ, ñîîòâåòñòâóþùèå ëîêàëè îòêàòà
    (fallback), òàêæå çàãðóæàþòñÿ è äîáàâëÿþòñÿ ê êàòàëîãó, åñëè îí åù¼ íå çàãðóæåí.  êîíå÷íîì èòîãå ïîëó÷àåòñÿ áîëüøîé ñëîâàðü ñ ïåðåâîäàìè. Ñì. òàêæå
    Êàòàëîãè ñîîáùåíèé.
    ˆ Åñëè ñîîáùåíèå åñòü â êàòàëîãå, âîçâðàùàåòñÿ åãî ïåðåâîä. Åñëè æå íåò, ïåðåâîä÷èê âîçâðàùàåò îðèãèíàë ñîîáùåíèÿ.
    Ïðè èñïîëüçîâàíèè ìåòîäà trans() Symfony2 èùåò ñòðîêó öåëèêîì â ïîäõîäÿùåì êàòàëîãå è âîçâðàùàåò åãî (åñëè åñòü ÷òî âîçâðàùàòü).
    Çàïîëíèòåëè â ñîîáùåíèÿõ

    Èíîãäà, ñîîáùåíèå, êîòîðîå íóæíî ïåðåâåñòè, ñîäåðæèò ïåðåìåííóþ:
    // ...
    public function indexAction($name)
    {
    $t = $this->get('translator')->trans('Hello '.$name);
    }

    return new Response($t);

    Òåì íå ìåíåå, ñîçäàíèå ïåðåâîäà äëÿ ýòîé ñòðîêè íåâîçìîæíî, òàê êàê ïåðåâîä÷èê áóäåò èñêàòü ñòðîêó öåëèêîì, âêëþ÷àÿ ïåðåìåííóþ (íàïðèìåð, Hello Ryan èëè Hello
    Fabien). Âìåñòî òîãî, ÷òîáû ïèñàòü ïåðåâîäû äëÿ êàæäîãî âîçìîæíîãî çíà÷åíèÿ ïåðåìåííîé $name, ìû ìîæåì çàìåíèòü ïåðåìåííóþ çàïîëíèòåëåì (aka placeholder):
    // ...
    public function indexAction($name)
    {
    $t = $this->get('translator')->trans('Hello %name%', array('%name%' => $name));
    }

    new Response($t);

    Symfony2 òåïåðü áóäåò èñêàòü ïåðåâîä îðèãèíàëà ñ çàïîëíèòåëåì (Hello %name%) è
    ëèøü çàòåì çàìåíÿòü çàïîëíèòåëü åãî ðåàëüíûì çíà÷åíèåì. Ñîçäàíèå ïåðåâîäà íå áóäåò îò òîãî, ÷òî âû äåëàëè ðàíåå:
    270

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    XML







    Hello %name%
    Bonjour %name%





    ˆ

    PHP

    // messages.fr.php
    return array(
    'Hello %name%' => 'Bonjour %name%',
    );

    ˆ

    YAML

    # messages.fr.yml
    'Hello %name%': Hello %name%

    Ïðèìå÷àíèå: Çàïîëíèòåëè ìîãóò èìåòü ëþáóþ ôîðìó, òàê êàê ïîëíîå ñîîáùåíèå

    âîññòàíàâëèâàåòñÿ ñ èñïîëüçîâàíèåì PHP-ôóíêöèè strtr function. Òåì íå ìåíåå, íîòàöèÿ %var% íåîáõîäèìà äëÿ èñïîëüçîâàíèè øàáëîíîâ Twig è, â êîíå÷íîì èòîãå, áîëåå
    ÷èòàáåëüíà.
    Êàê âû ìîãëè âèäåòü, ïðîöåññ ñîçäàíèÿ ïåðåâîäà ñîñòîèò èç äâóõ øàãîâ:
    1. Èçâëå÷åíèå ñîîáùåíèÿ, êîòîðîå íóæíî ïåðåâåñòè, ïåðåäàâ åãî â Translator.
    2. Ñîçäàíèå ïåðåâîäà ñîîáùåíèÿ äëÿ êàæäîé ëîêàëè, êîòîðóþ âû ñîáèðàåòåñü ïîääåðæèâàòü.
    Âòîðîé øàã âûïîëíÿåòñÿ ïîñðåäñòâîì ñîçäàíèÿ êàòàëîãîâ ñîîáùåíèé, êîòîðûå ñîäåðæàò ïåðåâîäû äëÿ ëþáîãî êîëè÷åñòâà ëîêàëåé.
    2.14.3 Êàòàëîãè ñîîáùåíèé

    Êîãäà ñîîáùåíèå ïåðåâîäèòñÿ, Symfony2 ñîáèðàåò êàòàëîã ñîîáùåíèé äëÿ ëîêàëè ïîëüçîâàòåëÿ è èùåò â í¼ì åãî ïåðåâîä. Êàòàëîã ñîîáùåíèé ñõîæ ñî ñëîâàð¼ì ïåðåâîäîâ
    2.14.

    Ïåðåâîäû

    271

    Symfony Documentation, Âûïóñê 2.0

    äëÿ íåêîòîðîé ëîêàëè. Íàïðèìåð, êàòàëîã äëÿ ëîêàëè fr_FR ìîæåò ñîäåðæàòü òàêîé
    ïåðåâîä:
    Symfony2 is Great => J'aime Symfony2
    Îáÿçàííîñòüþ ðàçðàáîò÷èêà (èëè ïåðåâîä÷èêà) èíòåðíàöèîíàëèçèðîâàííîãî ïðèëîæåíèÿ ÿâëÿåòñÿ ñîçäàíèå òàêèõ ïåðåâîäîâ. Ïåðåâîäû õðàíÿòñÿ â ôàéëîâîé ñèñòåìå è îáíàðóæèâàþòñÿ Symfony áëàãîäàðÿ íåêîòîðûì ñîãëàøåíèÿì.

    Ñîâåò: Êàæäûé ðàç, êîãäà âû ñîçäà¼òå

    ðåñóðñ ïåðåâîäîâ (èëè óñòàíàâëèâàåòå
    ïàêåò, êîòîðûé âêëþ÷àåò ïåðåâîäû), óáåäèòåñü, ÷òî âû î÷èñòèëè êýø, ÷òîáû Symfony
    ñìîã íàéòè íîâûå ðåñóðñû äëÿ ïåðåâîäà:
    íîâûé

    php app/console cache:clear

    Ïåðåâîäû: ðàñïîëîæåíèå â ïðîåêòå è ñîãëàøåíèÿ ïî èìåíîâàíèþ

    Symfony2 èùåò ôàéëû ñîîáùåíèé (ò.å. ïåðåâîäû) â äâóõ ìåñòàõ:
    ˆ Äëÿ ñîîáùåíèé âíóòðè ïàêåòà, ôàéëû ñîîáùåíèé äîëæíû áûòü ðàñïëîæåíû â
    äèðåêòîðèè Resources/translations/;
    ˆ Äëÿ ïåðåîïðåäåëåíèé ïåðåâîäîâ ëþáîãî ïàêåòà, ðàçìåñòèòå ôàéëû â äèðåêòîðèè
    app/Resources/translations.
    Íàèìåíîâàíèå ôàéëîâ ïåðåâîäîâ òàêæå âàæíî, òàê êàê Symfony2 èñïîëüçóåò ñîãëàøåíèå ïî îïðåäåëåíèþ äåòàëåé ïåðåâîäà. Êàæäûé ôàéë ñîîáùåíèé äîëæåí áûòü íàçâàí
    â ñîîòâåòñòâèè ñî ñëåäóþùèì øàáëîíîì: domain.locale.loader:
    ˆ domain: Íå îáÿçàòåëüíûé ïóòü äëÿ ñòðóêòóðèðîâàíèÿ ñîîáùåíèé â ãðóïïû (íàïðèìåð, admin, navigation èëè æå ïî óìîë÷àíèþ messages) - ñì. Èñïîëüçîâàíèå
    äîìåíîâ ñîîáùåíèé
    ˆ locale: Ëîêàëü, êîòîðîé ñîîòâåòñòâóåò ïåðåâîä (íàïðèìåð, en_GB, en, è ò.ä.);
    ˆ loader: Êàê Symfony2 äîëæåí çàãðóçèòü è ïàðñèòü ôàéë (íàïðèìåð, xliff, php
    èëè yml).
    Loader ìîæåò áûòü íàèìåíîâàíèåì ëþáîãî çàðåãèñòðèðîâàííîãî çàãðóç÷èêà. Ïî óìîë÷àíèþ â Symfony ïðåäñòàâëåíû ñëåäóþùèå çàãðóç÷èêè:
    ˆ xliff: XLIFF ôàéë;
    ˆ php: PHP ôàéë;
    ˆ yml: YAML ôàéë.
    Âûáîð çàãðóç÷èêà, êîòîðûé áóäåò èñïîëüçîâàí, çàâèñèò öåëèêîì îò âàñ è ïî ñóòè ýòî
    âîïðîñ âêóñà.
    272

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Âû òàêæå ìîæåòå õðàíèòü ïåðåâîäû â áàçå äàííûõ, èëè ëþáîì äðó-

    ãîì õðàíèëèùå ïðè ïîìîùè âàøåãî ñîáñòâåííîãî êëàññà, ðåàëèçóþùåãî èíòåðôåéñ
    Symfony\Component\Translation\Loader\LoaderInterface. Ñì. ñòàòüþ â êíèãå ðåöåïòîâ: Ïîëüçîâàòåëüñêèå çàãðóç÷èêè ïåðåâîäîâ.

    Ñîçäàíèå ïåðåâîäîâ

    Êàæäûé ôàéë ñîäåðæèò íàáîð ïàð id-translation äëÿ çàäàííîãî äîìåíà è ëîêàëè. Id
    - ýòî èäåíòèôèêàòîð åäèíè÷íîãî ïåðåâîäà è ìîæåò áûòü êàê ñîîáùåíèåì íà ÿçûêà
    áàçîâîé ëîêàëè (íàïðèìåð, Symfony is great) èëè æå íåêîòîðûì óíèêàëüíûì èäåíòèôèêàòîðîì (íàïðèìåð, symfony2.great - íèæå ìû åù¼ ñêàæåì îá ýòîì ïàðó ñëîâ):
    ˆ

    XML







    Symfony2 is great
    J'aime Symfony2


    symfony2.great
    J'aime Symfony2





    ˆ

    PHP

    // src/Acme/DemoBundle/Resources/translations/messages.fr.php
    return array(
    'Symfony2 is great' => 'J\'aime Symfony2',
    'symfony2.great'
    => 'J\'aime Symfony2',
    );

    ˆ

    YAML

    # src/Acme/DemoBundle/Resources/translations/messages.fr.yml
    Symfony2 is great: J'aime Symfony2
    symfony2.great:
    J'aime Symfony2

    2.14.

    Ïåðåâîäû

    273

    Symfony Documentation, Âûïóñê 2.0

    Symfony2 áóäåò íàõîäèòü ýòè ôàéëû è èñïîëüçîâàòü èõ ïðè ïåðåâîäå êàê Symfony2
    is great, òàê è symfony2.great ïðè èñïîëüçîâàíèè ôðàíöóçñêîé ëîêàëè (fr_FR èëè
    fr_BE).

    274

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Îáû÷íûå ôðàçû VS êëþ÷åâûå ñëîâà â ôàéëàõ ñîîáùåíèé
    Ýòîò ïðèìåð èëëþñòðèðóåò äâà ðàçëè÷íûõ ïîäõîäà ïî ñîçäàíèþ ïåðåâîäèìûõ ñîîáùåíèé:
    $t = $translator->trans('Symfony2 is great');
    $t = $translator->trans('symfony2.great');

    Ïðè èñïîëüçîâàíèè ïåðâîãî ìåòîäà, ñîîáùåíèå ïèøåòñÿ íà ÿçûêå ëîêàëè ïî óìîë÷àíèþ (â äàííîì ñëó÷àå ýòî àíãëèéñêèé). Ýòî æå ñîîáùåíèå çàòåì èñïîëüçóåòñÿ
    êàê id ïðè ñîçäàíèè ïåðåâîäîâ.
    Ïðè èñïîëüçîâàíèè âòîðîãî ìåòîäà, ñîîáùåíèå ýòî íåêîå êëþ÷åâîå ñëîâî, êîòîðîå
    ïåðåäà¼ò èäåþ ñîîáùåíèÿ. Êëþ÷åâîå ñëîâî äëÿ ñîîáùåíèÿ çàòåì èñïîëüçóåòñÿ â
    êà÷åñòâå id â ëþáîì ïåðåâîäå.  ýòîì ñëó÷àå, ïåðåâîä íåîáõîäèìî òàêæå ñäåëàòü
    è äëÿ ëîêàëè ïî óìîë÷àíèþ (ò.å. ïåðåâåñòè symfony2.great â Symfony2 is great).
    Âòîðîé ìåòîä áîëåå óäîáåí, òàê êàê êëþ÷ ñîîáùåíèÿ íå íóæíî èçìåíÿòü â êàæäîì
    ôàéëå ïåðåâîäà, åñëè âû ðåøèòå, ÷òî ñîîáùåíèå äëÿ ëîêàëè ïî óìîë÷àíèþ äîëæíî
    âûãëÿäåòü ñëåäóþùèì îáðàçîì: Symfony2 is really great.
    Âûáîð òîãî èëè èíîãî ìåòîäà - òàêæå öåëèêîì çàâèñèò îò âàñ, íî ìû áû ðåêîìåíäîâàëè èñïîëüçîâàòü âòîðîé ïîäõîä ñ êëþ÷åâûìè ñëîâàìè.
    Â äîïîëíåíèå ê ýòîìó, php è yaml ôîðìàòû ïîääåðæèâàþò âëîæåííûå id äëÿ òîãî
    ÷òîáû èñêëþ÷èòü ïîñòîÿííîå ïîâòîðåíèå ïðè èñïîëüçîâàíèè êëþ÷åâûõ ñëîâ:
    ˆ YAML
    symfony2:
    is:
    great: Symfony2 is great
    amazing: Symfony2 is amazing
    has:
    bundles: Symfony2 has bundles
    user:
    login: Login

    ˆ

    2.14.

    PHP

    return array(
    'symfony2' => array(
    'is' => array(
    'great' => 'Symfony2 is great',
    'amazing' => 'Symfony2 is amazing',
    ),
    'has' => array(
    'bundles' => 'Symfony2 has bundles',
    ),
    ),
    'user' => array(
    'login' => 'Login',
    ),
    );
    Ïåðåâîäû

    275

    Ìíîæåñòâåííûå óðîâíè ðàçäåëÿþòñÿ ïðè ïîìîùè òî÷êè (.) ìåæäó íèìè, òàêèì
    îáðàçîì ïðåäûäóùèé ïðèìåð ñîîòâåòñòâóåò òàêæå òàêîìó íàïèñàíèþ:
    ˆ YAML

    Symfony Documentation, Âûïóñê 2.0

    2.14.4 Èñïîëüçîâàíèå äîìåíîâ ñîîáùåíèé

    Êàê âû óæå âèäåëè, ôàéëû ñîîáùåíèé ñòðóêòóðèðîâàíû ïî ðàçëè÷íûì ëîêàëÿì, êîòîðûì ñîîòâåòñòâóþò èõ ïåðåâîäû. Ôàéëû ñîîáùåíèé ìîãóò áûòü òàêæå ñòðóêòóðèðîâàíû
    ïî äîìåíàì. Ïðè ñîçäàíèè ôàéëîâ ñîîáùåíèé, äîìåí - ýòî ïåðâàÿ ÷àñòü èìåíè ôàéëà.
    Äîìåí ïî óìîë÷àíèþ - messages. Íàïðèìåð, ïðåäïîëîæèì, ÷òî äëÿ ëó÷øåé îðãàíèçàöèè ôàéëîâ ïåðåâîäîâ îíè áûëè ðàçäåëåíû íà òðè ðàçëè÷íûå äîìåíà: messages, admin
    è navigation. Äëÿ ôðàíöóçñêîãî ïåðåâîäà áûëè ñîçäàíû ñëåäóþùèå ôàéëû ñîîáùåíèé:
    ˆ messages.fr.xliff
    ˆ admin.fr.xliff
    ˆ navigation.fr.xliff
    Êîãäà ïåðåâîäèòñÿ ñòðîêà íå èç äîìåíà ïî óìîë÷àíèþ (messages), âû ÿâíî äîëæíû
    óêàçàòü äîìåí òðåòüèì àðãóìåíòîì ôóíêöèè trans():
    $this->get('translator')->trans('Symfony2 is great', array(), 'admin');

    Symfony2 áóäåò òåïåðü èñêàòü ñîîáùåíèå â äîìåíå admin, ñîîòâåòñòâóþùåì ëîêàëè ïîëüçîâàòåëÿ.
    2.14.5 Ðàáîòà ñ ëîêàëüþ ïîëüçîâàòåëÿ

    Ëîêàëü òåêóùåãî ïîëüçîâàòåëÿ õðàíèòñÿ â ñåññèè è äîñòóïíà ïðè ïîìîùè ñåðâèñà
    session:
    $locale = $this->get('request')->getLocale();
    $this->get('request')->setLocale('en_US');

    Òàêæå âîçìîæíî õðàíèòü ëîêàëü â ñåññèè:
    $this->get('session')->set('_locale', 'en_US');

    Ëîêàëü ïî óìîë÷àíèþ è Ëîêàëü äëÿ îòêàòà

    Åñëè ëîêàëü â ñåññèè ÿâíî íå óêàçàíà, Translator áóäåò èñïîëüçîâàòü ïàðàìåòð
    fallback_locale. Ïî óìîë÷àíèþ ýòîò ïàðàìåòð óñòàíîâëåí â en (ñì. Íàñòðîéêà).
     êà÷åñòâå àëüòåðíàòèâû, âû ìîæåòå ãàðàíòèðîâàòü, ÷òî ëîêàëü áóäåò óñòàíîâëåíà â
    ñåññèè, åñëè îïðåäåëèòå ïàðàìåòð default_locale äëÿ ñåðâèñà ñåññèè:
    ˆ

    276

    YAML

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    # app/config/config.yml
    framework:
    default_locale: en

    ˆ

    XML



    en


    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    'default_locale' => 'en',
    ));

    Äîáàâëåíî â âåðñèè 2.1.
    Ëîêàëü è URL

    Òàê êàê ëîêàëü ïîëüçîâàòåëÿ õðàíèòñÿ â ñåññèè, âîçìîæíî âàì çàõî÷åòñÿ èñïîëüçîâàòü
    îäèí è òîò æå URL äëÿ îòîáðàæåíèÿ ðåñóðñà íà ëþáûõ äðóãèõ ÿçûêàõ, îñíîâûâàÿñü
    íà ëîêàëè ïîëüçîâàòåëÿ. Íàïðèìåð, http://www.example.com/contact áóäåò îòîáðàæàòü êîíòåíò íà àíãëèéñêîì äëÿ îäíîãî ïîëüçîâàòåëÿ, íà ôðàíöóçñêîì äëÿ äðóãîãî
    ïîëüçîâàòåëÿ. Ê ñîæàëåíèþ, ýòî íàðóøàåò îñíîâîïîëàãàþùåå ïðàâèëî Web: êàæäûé
    URL äîëæåí âîçâðàùàòü îäèí è òîò æå ðåñóðñ âíå çàâèñèìîñòè îò ïîëüçîâàòåëÿ. Äëÿ
    òîãî ÷òîáû óñóãóáèòü ïðîáëåìó, çàäóìàéòåñü - êàêóþ âåðñèþ êîíòåíòà äîëæíà áóäåò
    èíäåêñèðîâàòüñÿ ïîèñêîâèêàìè?
    Íàèëó÷øèì ðåøåíèåì ÿâëÿåòñÿ âêëþ÷åíèå ëîêàëè â URL. Ýòîò ìåòîä ïîëíîñòüþ ïîääåðæèâàåòñÿ ñèñòåìîé ìàðøðóòèçàöèè ïðè ïîìîùè ñïåöèàëüíîãî ïàðàìåòðà _locale:
    ˆ

    YAML

    contact:
    pattern: /{_locale}/contact
    defaults: { _controller: AcmeDemoBundle:Contact:index, _locale: en }
    requirements:
    _locale: en|fr|de

    ˆ

    XML


    AcmeDemoBundle:Contact:index
    en
    2.14.

    Ïåðåâîäû

    277

    Symfony Documentation, Âûïóñê 2.0

    en|fr|de


    ˆ

    PHP

    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    $collection = new RouteCollection();
    $collection->add('contact', new Route('/{_locale}/contact', array(
    '_controller' => 'AcmeDemoBundle:Contact:index',
    '_locale'
    => 'en',
    ), array(
    '_locale'
    => 'en|fr|de'
    )));
    return $collection;

    Ïðè èñïîëüçîâàíèè â ìàðøðóòå ïàðàìåòðà _locale, ñîîòâåòñòâóþùàÿ ëîêàëü áóäåò àâòîìàòè÷åñêè óñòàíîâëåíà â ïîëüçîâàòåëüñêîé ñåññèè. Äðóãèìè ñëîâàìè, åñëè ïîëüçîâàòåëü ïîñåùàåò URI /fr/contact, ëîêàëü fr áóäåò àâòîìàòè÷åñêè óñòàíîâëåíà äëÿ
    ïîëüçîâàòåëÿ â ñåññèè.
    Òåïåðü âû ìîæåòå èñïîëüçîâàòü ëîêàëü ïðè ñîçäàíèè ìàðøðóòîâ ê äðóãèì ïåðåâåä¼ííûì ñòðàíèöàì âàøåãî ïðèëîæåíèÿ.
    2.14.6 Ìíîæåñòâåííîå ÷èñëî äëÿ ñîîáùåíèé

    Ìíîæåñòâåííîå ÷èñëî äëÿ ñîîáùåíèé - ýòî ñëîæíûé âîïðîñ, òàê êàê ïðàâèëà ìîãóò
    áûòü î÷åíü ñëîæíûìè. Íàïðèìåð, íèæå ïðåäñòàâëåíî ìàòåìàòè÷åñêîå ïðåäñòàâëåíèå
    äëÿ ìíîæåñòâåííîãî ÷èñëà â ðóññêîì ÿçûêå:

    (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4

    Êàê âû ìîæåòå âèäåòü, â ðóññêîì ÿçûêå èìååòñÿ òðè ðàçëè÷íûõ ôîðìû ìíîæåñòâåííîãî
    ÷èñëà. Äëÿ êàæäîé ôîðìû ìíîæåñòâåííîå ÷èñëî áóäåò äðóãèì è ïîýòîìó ïåðåâîä òàêæå
    ñëîæåí.
    Êîãäà ïåðåâîä èìååò ðàçëè÷íûå ôîðìû èç-çà ìíîæåñòâåííîãî ÷èñëà, âû ìîæåòå ïðåäîñòàâèòü âñå ôîðìû â êà÷åñòâå ñòðîêè, ðàçäåë¼ííîé âåðòèêàëüíîé ÷åðòîé (|):
    'There is one apple|There are %count% apples'

    Äëÿ òîãî ÷òîáû ïåðåâîäèòü ñîîáùåíèÿ ñ ó÷¼òîì ìíîæåñòâåííîãî ÷èñëà, èñïîëüçóéòå
    ìåòîä:method:Symfony\Component\Translation\Translator::transChoice :

    278

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    // ...
    $t = $this->get('translator')->transChoice(
    'There is one apple|There are %count% apples',
    10,
    array('%count%' => 10)
    );

    Âòîðîé àðãóìåíò (10 â äàííîì ïðèìåðå), ýòî ÷èñëî îáúåêòîâ, êîòîðîå áóäåò èñïîëüçîâàòüñÿ äëÿ îïðåäåëåíèÿ êàêîé èìåííî ïåðåâîä áóäåò èñïîëüçîâàí, à òàêæå áóäåò çàìåùàòü %count%.
    Îñíîâûâàÿñü íà ýòîì ÷èñëå, ïåðåâîä÷èê âûáåðåò ïðàâèëüíóþ ôîðìó ìíîæåñòâåííîãî
    ÷èñëà.  àíãëèéñêîì ÿçûêå, ñëîâà â îñíîâíîì èìåþò ôîðìó åäèíñòâåííîãî ÷èñëà, êîãäà
    èìååòñÿ îäèí îáúåêò è ôîðìó ìíîæåñòâåííîãî ÷èñëà äëÿ ëþáîãî äðóãîãî ÷èñëà (0, 1,
    2...). Èòàê, åñëè count áóäåò 1, ïåðåâîä÷èê áóäåò èñïîëüçîâàòü ïåðâóþ ñòðîêó (There
    is one apple) â êà÷åñòâå ïåðåâîäà.  ïðîòèâíîì ñëó÷àå, îí áóäåò èñïîëüçîâàòü There
    are %count% apples.
    Ôðàíöóçñêèé ïåðåâîä áóäåò òàêèì:
    'Il y a %count% pomme|Il y a %count% pommes'

    Äàæå åñëè ýòè ñòðîêè âûãëÿäÿò ïîõîæèì îáðàçîì (ñîñòîÿò èç äâóõ ïîäñòðîê, ðàçäåë¼ííûõ âåðòèêàëüíîé ÷åðòîé), ôðàíöóçñêîå ïðàâèëî îòëè÷àåòñÿ: ïåðâàÿ ôîðìà (åäèíñòâåííîå ÷èñëî) èñïîëüçóåòñÿ åñëè count ðàâåí 0 èëè 1. Òàêèì îáðàçîì, ïåðåâîä÷èê àâòîìàòè÷åñêè áóäåò èñïîëüçîâàòü ïåðâóþ ñòðîêó (Il y a %count% pomme), êîãäà count
    áóäåò ðàâåí 0 èëè 1.
    Êàæäàÿ ëîêàëü èìååò ñâîé ñîáñòâåííûé íàáîð ïðàâèë, íåêîòîðûå èç êîòîðûõ èìåþò
    öåëûõ øåñòü ðàçëè÷íûõ ôîðì ìíîæåñòâåííîãî ÷èñëà ñî ñëîæíûìè ïðàâèëàìè, ëåæàùèìè â èõ îñíîâå. Ïðàâèëà ïðîñòû äëÿ àíãëèéñêîãî è ôðàíöóçñêîãî, íî â ðóññêîì ÿçûêå
    âû âåðîÿòíî çàõîòèòå çíàòü, êàêîå ïðàâèëî ñîîòâåòñòâóåò êàêîé ñòðîêå. Äëÿ òîãî ÷òîáû
    ïîìî÷ü ïåðåâîä÷èêó, âû ìîæåòå äîïîëíèòåëüíî äîáàâèòü òàã äëÿ êàæäîé ñòðîêè:
    'one: There is one apple|some: There are %count% apples'
    'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'

    Òàãè ÿâëÿþòñÿ ëèøü ïîäñêàçêàìè äëÿ ïåðåâîä÷èêà è íå âëèÿþò íà ëîãèêó, èñïîëüçóåìîé äëÿ îïðåäåëåíèÿ íóæíîé ôîðìû ìíîæåñòâåííîãî ÷èñëà. Òàãîì ìîæåò áûòü ëþáàÿ
    îïèñàòåëüíàÿ ñòðîêà, îêàí÷èâàþùàÿñÿ äâîåòî÷èåì (:). Òàãè òàêæå ìîãóò áûòü ðàçëè÷íûìè â îðèãèíàëüíîì ñîîáùåíèè è â ïåðåâîäå.

    2.14.

    Ïåðåâîäû

    279

    Symfony Documentation, Âûïóñê 2.0

    Ïîäðîáíåå î ìíîæåñòâåííîñòè (èíòåðâàëüíûé ìåòîä)

    Íàèáîëåå ïðîñòîé ïóòü ñîçäàíèÿ ìíîæåñòâåííîãî ÷èñëà äëÿ ñîîáùåíèÿ â Symfony2 èñïîëüçîâàòü âñòðîåííóþ ëîãèêó äëÿ âûáîðà ñòðîêè íà îñíîâå äàííîãî íîìåðà. Èíîãäà âàì ìîæåò ïîòðåáîâàòüñÿ áîëåå ïîëíûé êîíòðîëü íàä ïåðåâîäîì ìíîæåñòâåííûõ
    ÷èñåë èëè æå â îñîáûõ ñëó÷àÿõ òðåáóåòñÿ íå ñòàíäàðòíûé ïåðåâîä (äëÿ ÷èñëà 0 èëè
    æå äëÿ îòðèöàòåëüíûõ ÷èñåë, ê ïðèìåðó). Äëÿ òàêèõ ñëó÷àåâ âû ìîæåòå èñïîëüçîâàòü
    èíòåðâàëû:

    '{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf] There a

    Ýòè èíòåðâàëû ñëåäóþò íîòàöèè ISO 31-11. Ñòðîêà âûøå îïðåäåëÿåò ÷åòûðå ðàçëè÷íûõ
    èíòåðâàëà: òî÷íî 0, òî÷íî 1, 2-19, à òàêæå 20 è áîëåå.
    Âû òàêæå ìîæåòå êîìáèíèðîâàòü ÿâíûå ïðàâèëà è ñòàíäàðòíûå ïðàâèëà.  ýòîì ñëó÷àå,
    åñëè ÷èñëî íå ñîîòâåòñòâóåò óêàçàííûì èíòåðâàëàì, áóäåò èñïîëüçîâàíî ñòàíäàðòíîå
    ïðàâèëî:

    '{0} There are no apples|[20,Inf] There are many apples|There is one apple|a_few: There are %cou

    Íàïðèìåð, äëÿ îäíîãî ÿáëîêà áóäåò èñïîëüçîâàíî ñòàíäàðòíîå ïðàâèëî There is one
    apple. Äëÿ 2-19 - áóäåò èñïîëüçîâàíî âòîðîå ñòàíäàðòíîå ïðàâèëî There are %count%
    apples.
    Êëàññ Symfony\Component\Translation\Interval ìîæåò ïðåäñòàâëÿòü êîíå÷íûé íàáîð
    ÷èñåë:
    {1,2,3,4}

    Èëè æå ÷èñëî â èíòåðâàëå ìåæäó äâóìÿ ÷èñëàìè:
    [1, +Inf[
    ]-1,2[

    Ëåâàÿ ÷àñòü ðàçäåëèòåëÿ ìîæåò áûòü [ (âêëþ÷àÿ) èëè ] (èñêëþ÷àÿ). Ïðàâàÿ ÷àñòü
    ìîæåò áûòü [ (èñêëþ÷àÿ) or ] (âêëþ÷àÿ). Äëÿ áåñêîíå÷íîñòè âû ìîæåòå èñïîëüçîâàòü
    -Inf è +Inf.
    2.14.7 Ïåðåâîäû â øàáëîíàõ

    Îñíîâíóþ ÷àñòü âðåìåíè, ïåðåâîäû ïîÿâëÿþòñÿ â øàáëîíàõ. Symfony2 ïðåäîñòàâëÿåò
    ïîääåðæêó ïåðåâîäîâ êàê äëÿ Twig òàê è äëÿ PHP øàáëîíîâ.
    Twig øàáëîíû

    Symfony2 ïðåäîñòàâëÿåò ñïåöèàëèçèðîâàííûå òàãè äëÿ Twig (trans è transchoice) äëÿ
    òîãî ÷òîáû ïîìî÷ü ñ ïåðåâîäîì ñòàòè÷åñêèõ áëîêîâ òåêñòà :
    280

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    {% trans %} Hello %name%{% endtrans %}
    {% transchoice count %}
    {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
    {% endtranschoice %}

    Òàã transchoice àâòîìàòè÷åñêè ïîëó÷àåò ïåðåìåííóþ %count% èç êîíòåêñòà è ïåðåäà¼ò
    å¼ ïåðåâîä÷èêó. Ýòîò ìåõàíèçì ðàáîòàåò ëèøü êîãäà âû èñïîëüçóåòå çàïîëíèòåëü â
    ñòèëå %var%.

    Ñîâåò: Åñëè âàì íóæíî èñïîëüçîâàòü ñèìâîë ïðîöåíòà (%) â ñòðîêå, ýêðàíèðóéòå åãî
    ïðè ïîìîùè äóáëèðîâàíèÿ: {% trans %}Percent: %percent%%%{% endtrans %}

    Âû òàêæå ìîæåòå óêàçàòü äîìåí äëÿ ñîîáùåíèé è ïåðåäàòü íåêîòîðûå äîïîëíèòåëüíûå
    ïåðåìåííûå:
    {% trans with {'%name%': 'Fabien'} from "app" %} Hello %name%{% endtrans %}
    {% trans with {'%name%': 'Fabien'} from "app" into "fr" %} Hello %name%{% endtrans %}
    {% transchoice count with {'%name%': 'Fabien'} from "app" %}
    {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
    {% endtranschoice %}

    Ôèëüòðû trans è transchoice ìîãóò áûòü èñïîëüçîâàíû äëÿ ïåðåâîäà
    ìåííûõ è ñëîæíûõ âûðàæåíèé:

    òåêñòà ïåðå-

    {{ message | trans }}
    {{ message | transchoice(5) }}
    {{ message | trans({'%name%': 'Fabien'}, "app") }}
    {{ message | transchoice(5, {'%name%': 'Fabien'}, 'app') }}

    Ñîâåò: Èñïîëüçîâàíèå òàãîâ èëè ôèëüòðîâ äëÿ ïåðåâîäà èìååò îäèí è òîò æå ýôôåêò,

    íî ñ îäíèì íåáîëüøèì îòëè÷èåì: àâòîìàòè÷åñêîå ýêðàíèðîâàíèå âûâîäà ïðèìåíÿåòñÿ
    òîëüêî ê ïåðåìåííûì, ïåðåâåä¼ííûì ïðè ïîìîùè ôèëüòðà. Äðóãèìè ñëîâàìè, åñëè âàì
    íóæíî áûòü óâåðåííûìè, ÷òî âàøà ïåðåìåííàÿ íå ýêðàíèðîâàíà, âû äîëæíû ïðèìåíÿòü
    ôèëüòð raw ïîñëå ôèëüòðà trans:
    {# òåêñò ìåæäó òàãàìè íèêîãäà íå áóäåò ýêðàíèðîâàí #}
    {% trans %}

    foo


    {% endtrans %}

    2.14.

    Ïåðåâîäû

    281

    Symfony Documentation, Âûïóñê 2.0

    {% set message = '

    foo

    ' %}
    {# ïåðåìåííàÿ ïåðåâåä¼ííàÿ ïðè ïîìîùè ôèëüòðà ýêðàíèðîâàíà ïî óìîë÷àíèþ #}
    {{ message | trans | raw }}
    {# íî ñòàòè÷åñêàÿ ñòðîêà íèêîãäà íå ýêðàíèðóåòñÿ #}
    {{ '

    foo

    ' | trans }}

    PHP Øàáëîíû

    Ñåðâèñ-ïåðåâîä÷èê äîñòóïåí â PHP øàáëîíàõ ïðè ïîìîùè õåëïåðà translator:
    trans('Symfony2 is great') ?>
    transChoice(
    '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
    10,
    array('%count%' => 10)
    ) ?>

    2.14.8 Ôîðñèðîâàíèå ëîêàëè ïåðåâîä÷èêà

    Êîãäà ïåðåâîäèòñÿ ñîîáùåíèå, Symfony2 èñïîëüçóåò ëîêàëü èç ñåññèè ïîëüçîâàòåëÿ èëè
    æå fallback ëîêàëü, åñëè òðåáóåòñÿ. Âû òàêæå ìîæåòå âðó÷íóþ óêàçàòü ëîêàëü äëÿ
    ïåðåâîäà:
    $this->get('translator')->trans(
    'Symfony2 is great',
    array(),
    'messages',
    'fr_FR',
    );
    $this->get('translator')->trans(
    '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
    10,
    array('%count%' => 10),
    'messages',
    'fr_FR',
    );

    282

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    2.14.9 Ïåðåâîä êîíòåíòà èç áàçû äàííûõ

    Ïåðåâîä êîíòåíòà èç áàçû äàííûõ äîëæåí îáðàáàòûâàòüñÿ Doctrine ïðè ïîìîùè
    Translatable Extension. Èíôîðìàöèþ îá ýòîé áèáëèîòåêå âû ìîæåòå íàéòè â å¼ äîêóìåíòàöèè.
    2.14.10 Çàêëþ÷åíèå

    Ïðè ïîìîùè êîìïîíåíòà Translation, ñîçäàíèå èíòåðíàöèîíàëüíûõ ïðèëîæåíèé áîëüøå
    íå òðåáóåò áîëåçíåííîãî ïðîöåññà è ìîæåò áûòü äîñòèãíóòî ïðè ïîìîùè ñëåäóþùèõ
    øàãîâ:
    ˆ Èçâëåêèòå

    ñîîáùåíèÿ

    âàøåãî

    ïðèëîæåíèÿ,

    çàâåðíóâ

    êàæäîå

    â

    ìåòîäû
    :method:`Symfony\\Component\\Translation\\Translator::trans`
    èëè
    :method:`Symfony\\Component\\Translation\\Translator::transChoice`;

    ˆ Ïåðåâåäèòå êàæäîå ñîîáùåíèå äëÿ ðàçëè÷íûõ ëîêàëåé, ñîçäàâ ôàéëû ïåðåâîäîâ.
    Symfony2 íàéä¼ò è îáðàáîòàåò êàæäûé ôàéë òàê êàê èõ èìåíà ñëåäóþò ñïåöèôè÷åñêèì ñîãëàøåíèÿì;
    ˆ Óïðàâëÿéòå ëîêàëüþ ïîëüçîâàòåëÿ, êîòîðàÿ õðàíèòñÿ â ñåññèè.

    2.15 Êîíòåéíåð ñëóæá
     ñîâðåìåííîì PHP ïðèëîæåíèè ìíîæåñòâî îáúåêòîâ. Îäèí îáúåêò ìîæåò îáëåã÷àòü
    îòïðàâêó email'îâ, äðóãîé - ñîõðàíÿòü èíôîðìàöèþ â áàçå äàííûõ. Â âàøåì ïðèëîæåíèè âû ìîæåòå ñîçäàòü îáúåêò, êîòîðûé âåä¼ò ó÷¼ò òîâàðîâ, èëè æå îáúåêò, êîòîðûé
    îáðàáàòûâàåò äàííûå îò ñòîðîííèõ API. Çäåñü âàæíî ïîíèìàíèå òîãî, ÷òî ñîâðåìåííîå
    ïðèëîæåíèå âûïîëíÿåò ìíîæåñòâî ôóíêöèé è ñîñòîèò èç ìíîæåñòâà îáúåêòîâ, ðåàëèçóþùèõ ýòè ôóíêöèè.
     ýòîé ãëàâå ìû ïîãîâîðèì îá îñîáîì îáúåêòå â Symfony2, êîòîðûé ïîçâîëÿåò âàì ñîçäàâàòü ýêçåìïëÿðû, ñèñòåìàòèçèðîâàòü è ïîëó÷àòü ðàçëè÷íûå îáúåêòû âàøåãî ïðèëîæåíèÿ. Ýòîò îáúåêò, íàçûâàåìûé êîíòåéíåðîì ñëóæá, ïîçâîëèò âàì ñòàíäàðòèçèðîâàòü è
    öåíòðàëèçîâàòü ñïîñîá ñîçäàíèÿ îáúåêòîâ â âàøåì ïðèëîæåíèè. Êîíòåéíåð äåëàåò âàøó
    æèçíü ïðîùå, áûñòðåå è äåëàåò îñîáûé àêöåíò íà àðõèòåêòóðå, êîòîðàÿ ïðåäîñòàâëÿåò íåçàâèñèìûé, ãîòîâûé ê ïîâòîðíîìó èñïîëüçîâàíèþ êîä. Òàê êàê âñå êëàññû ÿäðà
    Symfony2 èñïîëüçóþò êîíòåéíåð, âû óçíàåòå, êàê ðàñøèðÿòü, íàñòðàèâàòü è èñïîëüçîâàòü ëþáîé îáúåêò Symfony2. Êîíòåéíåð ñëóæá â çíà÷èòåëüíîé ñòåïåíè îïðåäåëÿåò
    ñêîðîñòü è ðàñøèðÿåìîñòü Symfony2.
    È, â êîíöå êîíöîâ, íàñòðîéêà è èñïîëüçîâàíèå êîíòåéíåðà ñëóæá âåñüìà ïðîñòà.  êîíöå ýòîé ãëàâû âû áóäåòå ñ ë¼ãêîñòüþ ñîçäàâàòü âàøè ñîáñòâåííûå îáúåêòû ïðè ïîìîùè

    2.15.

    Êîíòåéíåð ñëóæá

    283

    Symfony Documentation, Âûïóñê 2.0

    êîíòåéíåðà è íàñòðàèâàòü îáúåêòû èç ñòîðîííèõ ïàêåòîâ. Âû áóäåòå ïèñàòü áîëåå òåñòèðóåìûé è ìåíåå çàïóòàííûé êîä, êîòîðûé ìîæíî áóäåò èñïîëüçîâàòü ïîâòîðíî ëèøü
    ïîòîìó, ÷òî êîíòåéíåð ñëóæá äåëàåò íàïèñàíèå õîðîøåãî êîäà ïðîñòûì.
    2.15.1 ×òî òàêîå ñëóæáà?

    Åñëè âêðàòöå, Ñëóæáà - ýòî íåêèé PHP îáúåêò, êîòîðûé âûïîëíÿåò êàêóþ-ëèáî ãëîáàëüíóþ çàäà÷ó. Ýòî íàèìåíîâàíèå èñïîëüçóåòñÿ â êîìïüþòåðíîé íàóêå äëÿ îïèñàíèÿ
    îáúåêòà, êîòîðûé ñîçäàí ñ íåêîòîðîé öåëüþ (íàïðèìåð, îòïðàâëÿòü email'û). Êàæäàÿ
    ñëóæáà èñïîëüçóåòñÿ â ëþáîì ìîäóëå ïðèëîæåíèÿ, ãäå áû âàì íè ïîíàäîáèëñÿ ôóíêöèîíàë, êîòîðûé ïðåäîñòàâëÿåò ñëóæáà. Âàì íå òðåáóåòñÿ äåëàòü íè÷åãî îñîáåííîãî, äëÿ
    òîãî ÷òîáû ñîçäàòü ñëóæáó: ïðîñòî ñîçäàéòå PHP êëàññ, ðåøàþùèé íåêóþ êîíêðåòíóþ
    çàäà÷ó. Ïîçäðàâëÿåì, Âû òîëüêî ÷òî ñîçäàëè ñëóæáó!

    Ïðèìå÷àíèå: Êàê ïðàâèëî, PHP îáúåêò ÿâëÿåòñÿ ñëóæáîé, åñëè îí èñïîëüçóåòñÿ â

    âàøåì ïðèëîæåíèè ãëîáàëüíî. Åäèíñòâåííàÿ ñëóæáà Mailer èñïîëüçóåòñÿ äëÿ îòïðàâêè
    email ñîîáùåíèé, â òî âðåìÿ êàê ìíîæåñòâî îáúåêòîâ Message, êîòîðûå ñîäåðæàò ýòè
    ñîîáùåíèÿ, ñëóæáàìè íå ÿâëÿþòñÿ. Òî÷íî òàê æå, îáúåêò Product íå ÿâëÿåòñÿ ñëóæáîé,
    íî îáúåêò, êîòîðûé ñîõðàíÿåò Product â áàçó äàííûõ - ÿâëÿåòñÿ ñëóæáîé.
    Òàê ãäå æå âûãîäà? Ìûñëèòü â òåðìèíàõ ñëóæá ïîëåçíî, êîãäà âû íà÷èíàåòå äóìàòü
    î ðàñïðåäåëåíèè êàæäîãî êóñî÷êà ôóíêöèîíàëà â âàøåì ïðèëîæåíèè ïî ðÿäó ñëóæá.
    Òàê êàê êàæäàÿ ñëóæáà âûïîëíÿåò åäèíñòâåííóþ ôóíêöèþ, âû ìîæåòå ëåãêî ïîëó÷èòü
    äîñòóï ê ëþáîé ñëóæáå è èñïîëüçîâàòü å¼ âîçìîæíîñòè òàì, ãäå ýòî òðåáóåòñÿ. Êàæäàÿ
    ñëóæáà ëåãêî òåñòèðóåòñÿ è íàñòðàèâàåòñÿ, òàê êàê îíà íå çàâèñèò îò ïðî÷åãî ôóíêöèîíàëà. Òàêîå ðàçäåëåíèå íà ñëóæáû íàçûâàåòñÿ Ñåðâèñ-îðèåíòèðîâàííàÿ àðõèòåêòóðà
    è îíà íå óíèêàëüíà íè â ðàìêàõ Symfony2, íè äàæå â ìàñøòàáå âñåãî PHP. Ñòðóêòóðèðîâàíèå âàøåãî ïðèëîæåíèÿ â âèäå íàáîðà íåçàâèñèìûõ ñëóæá - ýòî õîðîøî èçâåñòíàÿ,
    à òàêæå õîðîøî çàðåêîìåíäîâàâøàÿ ñåáÿ ïðàêòèêà. Çíàíèå ýòîé àðõèòåêòóðû áóäåò
    ïîëåçíî ëþáîìó õîðîøåìó ðàçðàáîò÷èêó âíå çàâèñèìîñòè îò ÿçûêà, íà êîòîðîì îí ïðîãðàììèðóåò.
    2.15.2 ×òî òàêîå êîíòåéíåð ñëóæá?

    (èëè æå êîíòåéíåð âíåäðåíèÿ çàâèñèìîñòè ) - ýòî òàêæå PHP îáúåêò, êîòîðûé óïðàâëÿåò ñîçäàíèåì ñëóæá (ò.å. îáúåêòîâ). Íàïðèìåð, ïîëîæèì ó âàñ åñòü
    ïðîñòîé PHP êëàññ, êîòîðûé îòïðàâëÿåò email ñîîáùåíèÿ. Íå èìåÿ êîíòåéíåðà ñëóæá,
    âû áóäåòå âûíóæäåíû âðó÷íóþ ñîçäàâàòü ýòîò îáúåêò òàì ãäå âàì ýòî ïîòðåáóåòñÿ:
    Êîíòåéíåð ñëóæá

    use Acme\HelloBundle\Mailer;

    284

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    $mailer = new Mailer('sendmail');
    $mailer->send('ryan@foobar.net', ... );

    Âûãëÿäèò íå ñëîæíî. Âàø âîîáðàæàåìûé êëàññ Mailer ïîçâîëÿåò óêàçàòü ìåòîä, èñïîëüçóåìûé äëÿ îòïðàâêè ñîîáùåíèé (íàïðèìåð, sendmail, smtp è ò.ä.). Íî ÷òî áóäåò,
    åñëè ïîòðåáóåòñÿ èñïîëüçîâàòü ýòó ñëóæáó ãäå-òî åù¼? Åñòåñòâåííî âàì íå çàõî÷åòñÿ
    êîíôèãóðèðîâàòü îáúåêò Mailer êàæäûé ðàç, êîãäà âàì ïîòðåáóåòñÿ åãî èñïîëüçîâàòü.
    ×òî, åñëè âàì ïîòðåáóåòñÿ èçìåíèòü òðàíñïîðò ñ sendmail íà smtp âî âñ¼ì âàøåì ïðèëîæåíèè? Ïîòðåáîâàëîñü áû èñêàòü âñå ìåñòà, ãäå ñîçäà¼òñÿ Mailer è èçìåíÿòü èõ.
    2.15.3 Ñîçäàíèå/íàñòðîéêà ñëóæá â êîíòåéíåðå

    Íàèëó÷øèì ðåøåíèåì íà ïðàêòèêå - ðàçðåøèòü êîíòåéíåðó ñëóæá ñîçäàòü îáúåêò
    Mailer äëÿ âàñ. Äëÿ ýòîãî êîíòåéíåð íåîáõîäèìî îáó÷èòü - êàê ñîçäàâàòü îáúåêò
    Mailer. Ýòî âûïîëíÿåòñÿ ïðè ïîìîùè êîíôèãóðàöèè, êîòîðóþ ìîæíî âûïîëíèòü â
    ôîðìàòàõ YAML, XML èëè PHP:
    ˆ

    YAML

    # app/config/config.yml
    services:
    my_mailer:
    class:
    Acme\HelloBundle\Mailer
    arguments:
    [sendmail]

    ˆ

    XML




    sendmail



    ˆ

    PHP

    // app/config/config.php
    use Symfony\Component\DependencyInjection\Definition;
    $container->setDefinition('my_mailer', new Definition(
    'Acme\HelloBundle\Mailer',
    array('sendmail')
    ));

    2.15.

    Êîíòåéíåð ñëóæá

    285

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå:

    Ïðè
    èíèöèàëèçàöèè
    Symfony2
    îí
    ñîçäà¼ò
    êîíòåéíåð
    ñëóæá,
    èñïîëüçóÿ
    êîíôèãóðàöèþ
    ïðèëîæåíèÿ
    (ïî
    óìîë÷àíèþ
    app/config/config.yml). Ôàéë, êîòîðûé áóäåò çàãðóæåí, îïðåäåëÿåòñÿ ìåòîäîì
    AppKernel::registerContainerConfiguration(), êîòîðûé çàãðóæàåò ôàéë, îòíîñÿùèéñÿ ê êîíêðåòíîìó îêðóæåíèþ (íàïðèìåð, config_dev.yml äëÿ dev èëè æå
    config_prod.yml äëÿ prod).
    Ýêçåìïëÿð îáúåêòà Acme\HelloBundle\Mailer òåïåðü ìîæíî ïîëó÷èòü ÷åðåç êîíòåéíåð
    ñëóæá. À ñàì êîíòåéíåð äîñòóïåí â ëþáîì òðàäèöèîííîì êîíòðîëëåðå Symfony2 ïðè
    ïîìîùè âñïîìîãàòåëüíîãî ìåòîäà get():
    class HelloController extends Controller
    {
    // ...
    public function sendEmailAction()
    {
    // ...
    $mailer = $this->get('my_mailer');
    $mailer->send('ryan@foobar.net', ... );
    }

    }

    Êîãäà çàïðàøèâàåòñÿ ñëóæáà my_mailer, êîíòåéíåð ñîçäà¼ò å¼ îáúåêò è âîçâðàùàåò å¼.
    Ýòî åù¼ îäíî ïðåèìóùåñòâî îò èñïîëüçîâàíèÿ êîíòåéíåðà ñëóæá. À èìåííî, ñëóæáà
    íå ñîçäà¼òñÿ âïëîòü äî òîãî ìîìåíòà, êîãäà îíà áóäåò íóæíà âàì. Åñëè âû îïðåäåëèòå
    ñëóæáó, íî íèãäå å¼ íå èñïîëüçóåòå - îíà íèêîãäà íå áóäåò ñîçäàíà. Ýòî ýêîíîìèò ïàìÿòü
    è äåëàåò âàøå ïðèëîæåíèå áûñòðåå. Ýòî òàêæå îçíà÷àåò, ÷òî âû ìîæåòå îïðåäåëÿòü
    ñêîëüêî óãîäíî ñëóæá áåç óùåðáà áûñòðîäåéñòâèþ ïðèëîæåíèÿ - ñëóæáû, êîòîðûå íå
    èñïîëüçóþòñÿ - íå áóäóò è ñîçäàíû.
     êà÷åñòâå ïðèÿòíîãî áîíóñà, ñëóæáà Mailer áóäåò ñîçäàíà ëèøü îäíàæäû è îäèí è
    òîò æå å¼ ýêçåìïëÿð áóäåò âîçâðàùàòüñÿ âñÿêèé ðàç, êîãäà âû çàïðàøèâàåòå äàííóþ
    ñëóæáó. Êàê ïðàâèëî, âû èìåííî ýòîãî è îæèäàåòå (òàêîé ïîäõîä áîëåå ãèáîê è óäîáåí),
    îäíàêî íèæå âû óçíàåòå êàê íàñòðîèòü ñëóæáó òàêèì îáðàçîì, ÷òîáû îíà ìîãëà èìåòü
    íåñêîëüêî ýêçåìïëÿðîâ îäíîâðåìåííî.
    2.15.4 Ïàðàìåòðû ñëóæáû

    Ñîçäàíèå íîâîé ñëóæáû (ò.å. îáúåêòà) ïðè ïîìîùè êîíòåéíåðà ïðîèñõîäèò î÷åíü ïðîñòî.
    Ïàðàìåòðû äåëàþò ñëóæáû áîëåå ãèáêèìè:
    ˆ

    286

    YAML

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    # app/config/config.yml
    parameters:
    my_mailer.class:
    my_mailer.transport:
    services:
    my_mailer:
    class:
    arguments:

    ˆ

    Acme\HelloBundle\Mailer
    sendmail

    %my_mailer.class%
    [%my_mailer.transport%]

    XML



    Acme\HelloBundle\Mailer
    sendmail



    %my_mailer.transport%



    ˆ

    PHP

    // app/config/config.php
    use Symfony\Component\DependencyInjection\Definition;
    $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');
    $container->setParameter('my_mailer.transport', 'sendmail');
    $container->setDefinition('my_mailer', new Definition(
    '%my_mailer.class%',
    array('%my_mailer.transport%')
    ));

    Êîíå÷íûé ðåçóëüòàò òàêîé æå, êàê è ðàíüøå - îòëè÷èå ëèøü â òîì, êàê îïðåäåëåíà
    ñëóæáà. Çàêëþ÷èâ ñòðîêè my_mailer.class è my_mailer.transport ìåæäó ñèìâîëàìè
    ïðîöåíòà (%), âû óêàçàëè êîíòåéíåðó èñêàòü ïàðàìåòðû ñ ýòèìè èìåíàìè. Êîãäà êîíòåéíåð ñîçäàí, îí èùåò çíà÷åíèå äëÿ êàæäîãî ïàðàìåòðà è èñïîëüçóåò èõ ïðè ñîçäàíèè
    ñëóæá.
    Íàçíà÷åíèå ïàðàìåòðîâ - ïåðåäà÷à èíôîðìàöèè âíóòðü ñëóæá. Êîíå÷íî æå, íåò íè÷åãî
    çàçîðíîãî â ñîçäàíèè ñëóæá áåç ïàðàìåòðîâ, òåì íå ìåíåå ïàðàìåòðû äàþò íåêîòîðûå
    ïðåèìóùåñòâà:

    2.15.

    Êîíòåéíåð ñëóæá

    287

    Symfony Documentation, Âûïóñê 2.0

    ˆ ðàçäåëåíèå è ñòðóêòóðèðîâàíèå âñåõ îïöèé ñëóæáû;
    ˆ çíà÷åíèÿ ïàðàìåòðîâ ìîãóò áûòü èñïîëüçîâàíû äëÿ ìíîæåñòâåííîãî îïðåäåëåíèÿ
    ñëóæá;
    ˆ ïðè ñîçäàíèè ñëóæáû â ïàêåòå (îá ýòîì áóäåò ÷óòü íèæå), èñïîëüçîâàíèå ïàðàìåòðîâ ïîçâîëÿåò ëåãêî íàñòðîèòü ñëóæáó â âàøåì ïðèëîæåíèè.
    Âûáîð - èñïîëüçîâàòü èëè íå èñïîëüçîâàòü ïàðàìåòðû - öåëèêîì ëåæèò íà âàñ. Êà÷åñòâåííûå ïàêåòû îò ñòîðîííèõ ðàçðàáîò÷èêîâ âñåãäà èñïîëüçóþò ïàðàìåòðû, òàê êàê
    îíè äåëàþò ñëóæáû áîëåå êîíôèãóðèðóåìûìè. Äëÿ ñëóæá â âàøåì ïðèëîæåíèè, òåì
    íå ìåíåå, âàì ìîæåò è íå ïîòðåáîâàòüñÿ òà ãèáêîñòü, êîòîðóþ äà¼ò èñïîëüçîâàíèå ïàðàìåòðîâ.
    Ìàññèâû ïàðàìåòðîâ

    Ïàðàìåòðû - ýòî íå îáÿçàòåëüíî ñòðîêè, ýòî òàêæå ìîãóò áûòü è ìàññèâû. Â ôîðìàòå
    XML âû äîëæíû èñïîëüçîâàòü àòðèáóò type=collection äëÿ ïàðàìåòðîâ-ìàññèâîâ.
    ˆ

    YAML

    # app/config/config.yml
    parameters:
    my_mailer.gateways:
    - mail1
    - mail2
    - mail3
    my_multilang.language_fallback:
    en:
    - en
    - fr
    fr:
    - fr
    - en

    ˆ

    XML




    mail1
    mail2
    mail3



    en
    fr

    288

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0



    fr
    en




    ˆ

    PHP

    // app/config/config.php
    use Symfony\Component\DependencyInjection\Definition;
    $container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3'));
    $container->setParameter('my_multilang.language_fallback',
    array('en' => array('en', 'fr'),
    'fr' => array('fr', 'en'),
    ));

    2.15.5 Èìïîðò êîíôèãóðàöèé êîíòåéíåðà

    Ñîâåò: Â ýòîì ðàçäåëå ìû áóäåì ññûëàòüñÿ íà ôàéëû êîíôèãóðàöèè ñëóæá, êàê íà
    íåêîòîðûå ðåñóðñû. Ýòî ïîä÷¼ðêèâàåò òîò ôàêò, ÷òî, õîòÿ áîëüøèíñòâî ðåñóðñîâ êîíôèãóðàöèè áóäóò ïðåäñòàâëåíû â âèäå ôàéëîâ (íàïðèìåð, YAML, XML, PHP), Symfony2
    íàñòîëüêî ãèáîê, ÷òî êîíôèãóðàöèÿ ìîæåò áûòü çàãðóæåíà ïðàêòè÷åñêè îòîâñþäó (íàïðèìåð, èç áàçû äàííûõ èëè äàæå ÷åðåç âíåøíèé âåá-ñåðâèñ).

    Êîíòåéíåð ñëóæá ñîçäà¼òñÿ ñ èñïîëüçîâàíèåì îäíîãî êîíôèãóðàöèîííîãî ðåñóðñà (ïî
    óìîë÷àíèþ app/config/config.yml). Âñå ïðî÷èå ðåñóðñû äëÿ ñëóæá (âêëþ÷àÿ ÿäðî
    Symfony2 è íàñòðîéêè ñòîðîííèõ ïàêåòîâ) äîëæíû èìïîðòèðîâàòüñÿ òåì èëè èíûì
    ñïîñîáîì. Ýòî äà¼ò âàì àáñîëþòíóþ ãèáêîñòü â íàñòðîéêå ñëóæá â âàøåì ïðèëîæåíèè.
    Íàñòðîéêè ñëóæá ìîãóò áûòü èìïîðòèðîâàíû äâóìÿ ðàçëè÷íûìè ñïîñîáàìè. Âîïåðâûõ, ìû ïîãîâîðèì î ìåòîäå, êîòîðûé âû áóäåòå â îñíîâíîì èñïîëüçîâàòü â âàøåì
    ïðèëîæåíèè: äèðåêòèâà imports. Â ñëåäóþùåé ñåêöèè ìû ðàññìîòðèì äðóãîé, áîëåå
    ãèáêèé ìåòîä, íàèáîëåå ïðåäïî÷òèòåëüíûé äëÿ èìïîðòà íàñòðîåê ñëóæá èç ñòîðîííèõ
    ïðèëîæåíèé.
    Èìïîðò êîíôèãóðàöèè ïðè ïîìîùè äèðåêòèâû

    imports

    Ðàíåå âû ðàçìåñòèëè îïðåäåëåíèå ñëóæáû my_mailer íàïðÿìóþ â ôàéëå êîíôèãóðàöèè
    ïðèëîæåíèÿ (app/config/config.yml). Ïîñêîëüêó êëàññ Mailer ðàñïîëàãàåòñÿ â ïàêåòå
    2.15.

    Êîíòåéíåð ñëóæá

    289

    Symfony Documentation, Âûïóñê 2.0

    AcmeHelloBundle, èìååò ñìûñë ðàçìåñòèòü îïðåäåëåíèå êîíòåéíåðà my_mailer âíóòðè
    ýòîãî ïàêåòà.
    Âî-ïåðâûõ, ïåðåìåñòèòå îïðåäåëåíèå my_mailer â íîâûé ôàéë âíóòðè AcmeHelloBundle.
    Åñëè äèðåêòîðèè Resources èëè Resources/config îòñóòñòâóþò - ñîçäàéòå èõ.
    ˆ

    YAML

    # src/Acme/HelloBundle/Resources/config/services.yml
    parameters:
    my_mailer.class:
    Acme\HelloBundle\Mailer
    my_mailer.transport: sendmail
    services:
    my_mailer:
    class:
    arguments:

    ˆ

    %my_mailer.class%
    [%my_mailer.transport%]

    XML



    Acme\HelloBundle\Mailer
    sendmail



    %my_mailer.transport%



    ˆ

    PHP

    // src/Acme/HelloBundle/Resources/config/services.php
    use Symfony\Component\DependencyInjection\Definition;
    $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');
    $container->setParameter('my_mailer.transport', 'sendmail');
    $container->setDefinition('my_mailer', new Definition(
    '%my_mailer.class%',
    array('%my_mailer.transport%')
    ));

    Ñàìî îïðåäåëåíèå ñëóæáû îñòàëîñü áåç èçìåíåíèé, èçìåíèëîñü òîëüêî ðàñïîëîæåíèå
    ôàéëà êîíôèãóðàöèè. Êîíå÷íî æå, êîíòåéíåð ïîêà íè÷åãî íå çíàåò î íîâîì ðåñóðñå. Ê
    ñ÷àñòüþ, âû ìîæåòå ëåãêî èìïîðòèðîâàòü ôàéë ðåñóðñà ïðè ïîìîùè êëþ÷åâîãî ñëîâà
    290

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    imports â êîíôèãóðàöèè ïðèëîæåíèÿ:
    ˆ

    YAML

    # app/config/config.yml
    imports:
    hello_bundle:
    resource: @AcmeHelloBundle/Resources/config/services.yml

    ˆ

    XML






    ˆ

    PHP

    // app/config/config.php
    $this->import('@AcmeHelloBundle/Resources/config/services.php');

    Äèðåêòèâà imports ïîçâîëÿåò ïðèëîæåíèþ ïîäêëþ÷àòü ðåñóðñû êîíôèãóðàöèè êîíòåéíåðà ñëóæá èç ðàçëè÷íûõ ìåñò (êàê ïðàâèëî, èç ïàêåòîâ). Ðàñïîëîæåíèå resource äëÿ
    ôàéëîâ - ýòî àáñîëþòíûé ïóòü ê ýòîìó ôàéëó. Ñïåöèàëüíûé ñèíòàêñèñ @AcmeHello ñîîòâåòñòâóåò ïóòè ê äèðåêòîðèè ïàêåòà AcmeHelloBundle. Ýòî ïîìîãàåò óêàçûâàòü ïóòü
    ê ðåñóðñó íå çàáîòÿñü î òîì, ÷òî ïàêåò AcmeHelloBundle ìîæåò áûòü â áóäóùåì ïåðåìåù¼í â äðóãîå ìåñòî.
    Èìïîðò êîíôèãóðàöèè ïðè ïîìîùè ðàñøèðåíèé êîíòåéíåðà

    Ïðè ðàçðàáîòêå ñ èñïîëüçîâàíèåì Symfony2 ÷àùå âñåãî âû áóäåòå èñïîëüçîâàòü äèðåêòèâó imports äëÿ èìïîðòà êîíôèãóðàöèè êîíòåéíåðà èç ïàêåòîâ, êîòîðûå âû ñîçäàëè
    ñïåöèàëüíî äëÿ âàøåãî ïðèëîæåíèÿ. Êîíôèãóðàöèÿ êîíòåéíåðà äëÿ ñòîðîííèõ ïàêåòîâ, âêëþ÷àÿ ñëóæáû ÿäðà Symfony2, êàê ïðàâèëî, çàãðóæàåòñÿ ïðè ïîìîùè äðóãîãî
    ìåòîäà, áîëåå ãèáêîãî è ïðîñòî äëÿ íàñòðîéêè â âàøåì ïðèëîæåíèè.
    Âîò êàê ðàáîòàåò ýòîò ìåòîä. Âíóòðè êàæäîãî ïàêåòà åãî ñëóæáû îïðåäåëÿþòñÿ î÷åíü
    ïîõîæèì îáðàçîì, êàê âû äåëàëè ýòî ðàíåå. À èìåííî, ïàêåò èñïîëüçóåò îäèí èëè áîëåå
    ôàéëîâ êîíôèãóðàöèè (êàê ïðàâèëî, XML) äëÿ óêàçàíèÿ ïàðàìåòðîâ è ñëóæá ýòîãî ïàêåòà. Òåì íå ìåíåå, âìåñòî òîãî, ÷òîáû èìïîðòèðîâàòü êàæäûé ðåñóðñ íåïîñðåäñòâåííî
    â ôàéë êîíôèãóðàöèè âàøåãî ïðèëîæåíèÿ ïðè ïîìîùè äèðåêòèâû imports, âû ìîæåòå
    âûçâàòü ðàñøèðåíèå êîíòåéíåðà ñëóæá âíóòðè ïàêåòà, êîòîðîå âûïîëíèò ýòó ðàáîòó
    çà âàñ. Ðàñøèðåíèå êîíòåéíåðà ñëóæá - ýòî PHP êëàññ, ñîçäàííûé àâòîðîì ïàêåòà äëÿ
    âûïîëíåíèÿ ñëåäóþùèõ ôóíêöèé:
    ˆ Èìïîðòà âñåõ ðåñóðñîâ êîíòåéíåðà ñëóæá, íåîáõîäèìûõ äëÿ êîíôèãóðàöèè âñåõ
    ñëóæá ïàêåòîâ;
    2.15.

    Êîíòåéíåð ñëóæá

    291

    Symfony Documentation, Âûïóñê 2.0

    ˆ Ïðåäîñòàâëåíèÿ ïðîñòîé è ïîíÿòíîé êîíôèãóðàöèè, ïðè ïîìîùè êîòîðîé ïàêåò ìîæíî íàñòðîèòü, íå âçàèìîäåéñòâóÿ íàïðÿìóþ ñ êîíôèãóðàöèåé êîíòåéíåðà
    ñëóæá ïàêåòà.
    Äðóãèìè ñëîâàìè, ðàñøèðåíèå êîíòåéíåðà ñëóæá íàñòðàèâàåò ñëóæáû ïàêåòà îò âàøåãî
    èìåíè. È, êàê âû ñêîðî óâèäèòå, ðàñøèðåíèå ïðåäîñòàâëÿåò óäîáíûé âûñîêîóðîâíåâûé
    èíòåðôåéñ äëÿ íàñòðîéêè ïàêåòà.
    Âîçüì¼ì â êà÷åñòâå ïðèìåðà FrameworkBundle - îñíîâó Symfony2. Íàëè÷èå ñëåäóþùåãî êîäà â êîíôèãóðàöèè âàøåãî ïðèëîæåíèÿ âûçûâàåò ðàñøèðåíèå êîíòåéíåðà ñëóæá
    âíóòðè FrameworkBundle:
    ˆ

    YAML

    # app/config/config.yml
    framework:
    secret:
    xxxxxxxxxx
    charset:
    UTF-8
    form:
    true
    csrf_protection: true
    router:
    { resource: "%kernel.root_dir%/config/routing.yml" }
    # ...

    ˆ

    XML









    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('framework', array(
    'secret'
    => 'xxxxxxxxxx',
    'charset'
    => 'UTF-8',
    'form'
    => array(),
    'csrf-protection' => array(),
    'router'
    => array('resource' => '%kernel.root_dir%/config/routing.php'),
    // ...
    ));

    Êîãäà ïàðñèòñÿ êîíôèãóðàöèîííûé ôàéë, êîíòåéíåð èùåò ðàñøèðåíèå, êîòîðîå ìîæåò îáðàáîòàòü äèðåêòèâó framework. Âûçûâàåòñÿ ðàñøèðåíèå, êîòîðîå íàõîäèòñÿ
    â FrameworkBundle è çàãðóæàåòñÿ êîíôèãóðàöèÿ ñëóæá äëÿ FrameworkBundle. Åñëè
    âû óäàëèòå êëþ÷ framework èç êîíôèãóðàöèè ïðèëîæåíèÿ - îñíîâíûå ñåðâèñû ÿäðà
    292

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Symfony2 íå áóäóò çàãðóæåíû. Ñóòü æå çàêëþ÷àåòñÿ â òîì, ÷òî âñ¼ íàõîäèòñÿ ïîä âàøèì êîíòðîëåì: Symfony2 íå ñîäåðæèò íèêàêîé ìàãèè è íå äåëàåò íè÷åãî òàêîãî, ÷åãî
    áû âû íå êîíòðîëèðîâàëè.
    Êîíå÷íî æå, âû ìîæåòå äåëàòü ìíîãî áîëüøå, ÷åì ïðîñòî àêòèâèðîâàòü ðàñøèðåíèå
    êîíòåéíåðà ñëóæá äëÿ FrameworkBundle. Êàæäîå ðàñøèðåíèå ïîçâîëÿåò âàì ëåãêî íàñòðîèòü ïàêåò, íå çàáîòÿñü î òîì, êàê èìåííî îïðåäåëåíû åãî ñëóæáû.
    ñëó÷àå, ðàñøèðåíèå ïîçâîëÿåò íàñòðîèòü charset, error_handler,
    csrf_protection, router è ìíîãîå äðóãîå. Âíóòðè FrameworkBundle èñïîëüçóåò îïöèè, óêàçàííûå â íàñòðîéêàõ ïðèëîæåíèÿ äëÿ îïðåäåëåíèÿ è êîíôèãóðèðîâàíèÿ ñâîèõ ñëóæá. Ïàêåò ïîçàáîòèòñÿ î ñîçäàíèè âñåõ íåîáõîäèìûõ íàñòðîåê parameters è
    services äëÿ êîíòåéíåðà ñëóæá, ïðè ýòîì òàêæå ñîõðàíÿÿ ãèáêîñòü íàñòðîéêè.  êà÷åñòâå áîíóñà áîëüøèíñòâî ðàñøèðåíèé êîíòåéíåðà ñëóæá òàêæå âûïîëíÿþò âàëèäàöèþ
    - óâåäîìëÿþò âàñ îá îòñóòñòâóþùèõ èëè æå èìåþùèõ íåïðàâèëüíûé òèï ïàðàìåòðàõ.

    Â

    íàøåì

    Êîãäà âû óñòàíàâëèâàåòå èëè íàñòðàèâàåòå ïàêåò - ñìîòðèòå åãî äîêóìåíòàöèþ, ÷òîáû óçíàòü êàê óñòàíîâèòü è íàñòðîèòü åãî ñëóæáû. Îïöèè, äîñòóïíûå äëÿ îñíîâíûõ
    ïàêåòîâ ÿäðà âû ìîæåòå ïîñìîòðåòü â ñïðàâî÷íèêå .

    Ïðèìå÷àíèå: Êîíòåéíåð ñëóæá ðàñïîçíà¼ò ëèøü äèðåêòèâû parameters, services,
    è imports. Âñå îñòàëüíûå äèðåêòèâû îáðàáàòûâàþòñÿ ðàñøèðåíèÿìè.

    2.15.6 Èñïîëüçîâàíèå îäíèõ ñëóæá âíóòðè äðóãèõ (Âíåäðåíèå
    ñëóæá)

    Ðàññìîòðåííàÿ âûøå ñëóæáà my_mailer ïðîñòà: îíà ïðèíèìàåò ëèøü îäèí àðãóìåíò
    êîíñòðóêòîðà, êîòîðûé ëåãêî íàñòðàèâàåòñÿ. Êàê âû óâèäèòå, ñâîþ ñèëó êîíòåéíåð
    ïîêàçûâàåò, êîãäà âàì íóæíî ñîçäàòü ñëóæáó, êîòîðàÿ çàâèñèò îò îäíîé èëè íåñêîëüêèõ
    ñëóæá êîíòåéíåðà.
    Äàâàéòå íà÷í¼ì ñ ïðèìåðà. Ïðåäïîëîæèì ó âàñ åñòü íîâàÿ ñëóæáà NewsletterManager,
    êîòîðàÿ ïîìîãàåò ïîäãîòàâëèâàòü è ðàññûëàòü email-ñîîáùåíèÿ íà íåêîòîðûé íàáîð
    àäðåñîâ. Ñëóæáó my_mailer áûëî áû íåïëîõî èñïîëüçîâàòü äëÿ îòïðàâêè ñîîáùåíèé
    âíóòðè ñëóæáû NewsletterManager. Òàêèì îáðàçîì, êëàññ ìîæåò âûãëÿäåòü ïðèìåðíî
    òàê:
    namespace Acme\HelloBundle\Newsletter;
    use Acme\HelloBundle\Mailer;
    class NewsletterManager
    {
    protected $mailer;
    2.15.

    Êîíòåéíåð ñëóæá

    293

    Symfony Documentation, Âûïóñê 2.0

    public function __construct(Mailer $mailer)
    {
    $this->mailer = $mailer;
    }
    // ...

    }

    Íå èñïîëüçóÿ êîíòåéíåð ñëóæá, ìû ìîæåò ñîçäàòü NewsletterManager âíóòðè êîíòðîëëåðà:
    public function sendNewsletterAction()
    {
    $mailer = $this->get('my_mailer');
    $newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);
    // ...
    }

    Òàêîé ïîäõîä, â îáùåì-òî, íå ïëîõ, íî ÷òî, åñëè ïîòðåáóåòñÿ äîáàâèòü ê êîíñòðóêòîðó
    êëàññà NewsletterManager âòîðîé èëè äàæå òðåòèé àðãóìåíò? ×òî, åñëè âû ðåøèòå
    âûïîëíèòü ðåôàêòîðèíã è ïåðåèìåíóåòå êëàññ?  îáîèõ ñëó÷àÿõ âàì ïîòðåáîâàëîñü áû
    íàéòè âñå ìåñòà, ãäå ñîçäàþòñÿ ýêçåìïëÿðû NewsletterManager è èçìåíèòü èõ. È òóò
    êîíòåéíåð ñëóæá ïðåäîñòàâëÿåò âàì óäîáíîå ðåøåíèå:
    ˆ

    YAML

    # src/Acme/HelloBundle/Resources/config/services.yml
    parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
    services:
    my_mailer:
    # ...
    newsletter_manager:
    class:
    %newsletter_manager.class%
    arguments: [@my_mailer]

    ˆ

    XML




    Acme\HelloBundle\Newsletter\NewsletterManager


    294

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0









    ˆ

    PHP

    // src/Acme/HelloBundle/Resources/config/services.php
    use Symfony\Component\DependencyInjection\Definition;
    use Symfony\Component\DependencyInjection\Reference;

    // ...
    $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\Newslette
    $container->setDefinition('my_mailer', ... );
    $container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(new Reference('my_mailer'))
    ));

    Â YAML, ñïåöèàëüíûé ñèíòàêñèñ @my_mailer ñîîáùàåò êîíòåéíåðó, ÷òî íóæíî èñêàòü
    ñëóæáó my_mailer è ïåðåäàòü ýòîò îáúåêò â êîíñòðóêòîð NewsletterManager. Â ýòîì
    ñëó÷àå ñëóæáà my_mailer äîëæíà ñóùåñòâîâàòü. Åñëè å¼ îïðåäåëåíèå íå áóäåò íàéäåíî,
    áóäåò âûçâàíî èñêëþ÷åíèå. Âû òàêæå ìîæåòå ïîìåòèòü çàâèñèìîñòè îïöèîíàëüíûìè ýòî áóäåò îáñóæäàòüñÿ â ñëåäóþùåé ñåêöèè.
    Èñïîëüçîâàíèå ññûëîê íà ñëóæáû (âíåäðåíèå ñëóæá) - ýòî ìîùíûé èíñòðóìåíò, êîòîðûé ïîçâîëÿåò ñîçäàâàòü íåçàâèñèìûå êëàññû ñëóæá ñ ÷¼òêî îïðåäåë¼ííûìè çàâèñèìîñòÿìè.  ýòîì ïðèìåðå, ñëóæáå newsletter_manager äëÿ ôóíêöèîíèðîâàíèÿ íåîáõîäèìà ñëóæáà my_mailer. Êîãäà âû îïðåäåëèòå ýòó çàâèñèìîñòü â êîíòåéíåðå ñëóæá, îí
    ïîçàáîòèòñÿ î ñîçäàíèè âñåõ íåîáõîäèìûõ îáúåêòîâ.
    Îïöèîíàëüíûå çàâèñèìîñòè

    Âíåäðåíèå çàâèñèìîñòåé â êîíñòðóêòîð - ýòî ïðåêðàñíûé ñïîñîá óäîñòîâåðèòüñÿ, ÷òî
    çàâèñèìîñòü äîñòóïíà äëÿ èñïîëüçîâàíèÿ. Åñëè ó âàñ åñòü íåîáÿçàòåëüíûå çàâèñèìîñòè
    äëÿ êëàññà, òî ëó÷øèì âûáîðîì áóäåò èñïîëüçîâàíèå setter injection. Ýòî îçíà÷àåò,
    ÷òî âíåäðåíèå çàâèñèìîñòè ïðîèçâîäèòñÿ ïðè ïîìîùè íåêîòîðîãî ìåòîäà, à íå â êîíñòðóêòîðå. Êëàññ áóäåò âûãëÿäåòü ñëåäóþùèì îáðàçîì:
    namespace Acme\HelloBundle\Newsletter;

    2.15.

    Êîíòåéíåð ñëóæá

    295

    Symfony Documentation, Âûïóñê 2.0

    use Acme\HelloBundle\Mailer;
    class NewsletterManager
    {
    protected $mailer;
    public function setMailer(Mailer $mailer)
    {
    $this->mailer = $mailer;
    }
    // ...

    }

    Âíåäðåíèå çàâèñèìîñòè ïðè ïîìîùè ìåòîäà òðåáóåò òàêæå èçìåíåíèÿ ñèíòàêñèñà:
    ˆ

    YAML

    # src/Acme/HelloBundle/Resources/config/services.yml
    parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
    services:
    my_mailer:
    # ...
    newsletter_manager:
    class:
    %newsletter_manager.class%
    calls:
    - [ setMailer, [ @my_mailer ] ]

    ˆ

    XML




    Acme\HelloBundle\Newsletter\NewsletterManager










    296

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0



    ˆ

    PHP

    // src/Acme/HelloBundle/Resources/config/services.php
    use Symfony\Component\DependencyInjection\Definition;
    use Symfony\Component\DependencyInjection\Reference;

    // ...
    $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\Newslette
    $container->setDefinition('my_mailer', ... );
    $container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%'
    ))->addMethodCall('setMailer', array(
    new Reference('my_mailer')
    ));

    Ïðèìå÷àíèå: Ïîäõîäû ê âíåäðåíèþ ñëóæá, ïðåäñòàâëåííûå â ýòîé ñåêöèè íàçûâàþòñÿ constructor injection è setter injection. Êîíòåéíåð ñëóæá Symfony2 òàêæå ïîääåðæèâàåò property injection.

    2.15.7 Äåëàåì ññûëêè íà ñëóæáû îïöèîíàëüíûìè

    Èíîãäà, ñëóæáû ìîãóò èìåòü îïöèîíàëüíûå çàâèñèìîñòè, ò.å. çàâèñèìîñòü íå òðåáóåòñÿ,
    äëÿ òîãî, ÷òîáû ñëóæáà ïðàâèëüíî ðàáîòàëà.  ïðèìåðå âûøå ñëóæáà my_mailer äîëæíà ñóùåñòâîâàòü, â ïðîòèâíîì ñëó÷àå áóäåò ñãåíåðèðîâàíà îøèáêà (èñêëþ÷åíèå). Èçìåíèâ îïðåäåëåíèå ñëóæáû newsletter_manager âû ìîæåòå ñäåëàòü çàâèñèìîñòü íåîáÿçàòåëüíîé. Êîíòåéíåð áóäåò âíåäðÿòü å¼ ëèøü êîãäà ýòà çàâèñèìîñòü ñóùåñòâóåò, â ñëó÷àå
    æå åñëè îíà íå ñóùåñòâóåò, íèêàêèõ äåéñòâèé ïðîèçâîäèòüñÿ íå áóäåò:
    ˆ

    YAML

    # src/Acme/HelloBundle/Resources/config/services.yml
    parameters:
    # ...
    services:
    newsletter_manager:
    class:
    %newsletter_manager.class%
    arguments: [@?my_mailer]

    ˆ

    2.15.

    XML

    Êîíòåéíåð ñëóæá

    297

    Symfony Documentation, Âûïóñê 2.0











    ˆ

    PHP

    // src/Acme/HelloBundle/Resources/config/services.php
    use Symfony\Component\DependencyInjection\Definition;
    use Symfony\Component\DependencyInjection\Reference;
    use Symfony\Component\DependencyInjection\ContainerInterface;

    // ...
    $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\Newslette
    $container->setDefinition('my_mailer', ... );
    $container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(new Reference('my_mailer', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
    ));

    Â YAML, ñïåöèàëüíûé ñèíòàêñèñ @? ñîîáùàåò êîíòåéíåðó ñëóæá, ÷òî çàâèñèìîñòü íå
    îáÿçàòåëüíàÿ. È, êîíå÷íî æå, êîíñòðóêòîð NewsletterManager äîëæåí áûòü ïåðåïèñàí,
    ÷òîáû ïîääåðæèâàòü îïöèîíàëüíóþ çàâèñèìîñòü:
    // ...
    public function __construct(Mailer $mailer = null)
    {
    // ...
    }

    2.15.8 Îñíîâíûå ñëóæáû Symfony è ñëóæáû îò ñòîðîííèõ ðàçðàáîò÷èêîâ

    Òàê êàê Symfony2 è âñå ñòîðîííèå ïàêåòû íàñòðàèâàþò è ïîëó÷àþò ñâîè ñëóæáû ïðè
    ïîìîùè êîíòåéíåðà, âû ìîæåòå ëåãêî ïîëó÷èòü äîñòóï ê íèì èëè äàæå èñïîëüçîâàòü
    èõ â ñâîèõ ñîáñòâåííûõ ñëóæáàõ. Äëÿ ïðîñòîòû Symfony2 ïî óìîë÷àíèþ íå òðåáóåò,
    ÷òîáû êîíòðîëëåðû áûëè áû îïðåäåëåíû êàê ñëóæáû. Êðîìå òîãî, Symfony2 âíåäðÿåò
    298

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    â âàø êîíòðîëëåð êîíòåéíåð ñëóæá öåëèêîì. Íàïðèìåð, äëÿ ðàáîòû ñ ïîëüçîâàòåëüñêîé
    ñåññèåé, Symfony2 ïðåäîñòàâëÿåò ñëóæáó session, êîòîðàÿ ïîçâîëÿåò ïîëó÷èòü äîñòóï
    ê ñåññèè âíóòðè îáû÷íîãî êîíòðîëëåðà:
    public function indexAction($bar)
    {
    $session = $this->get('session');
    $session->set('foo', $bar);
    }

    // ...

    Â Symfony2 âû ïîñòîÿííî áóäåòå ïîëüçîâàòüñÿ ñëóæáàìè, ïðåäîñòàâëÿåìûìè ÿäðîì
    Symfony èëè æå ïðî÷èìè ïàêåòàìè îò ñòîðîííèõ ðàçðàáîò÷èêîâ, äëÿ âûïîëíåíèÿ òàêèõ
    çàäà÷ êàê îòîáðàæåíèå øàáëîíîâ (templating), îòïðàâêó ìàéëîâ (mailer) èëè äîñòóï
    ê ïåðåìåííûì çàïðîñà (request).
    Âû ìîæåòå ïîéòè åù¼ äàëüøå è èñïîëüçîâàòü ýòè ñëóæáû âíóòðè âàøèõ ñëóæá, ñîçäàííûõ äëÿ âàøåãî ïðèëîæåíèÿ. Äàéòå èçìåíèì êëàññ NewsletterManager, ÷òîáû îí
    èñïîëüçîâàë ñòàíäàðòíûé mailer Symfony2 (âìåñòî my_mailer). Äàâàéòå òàêæå âíåäðèì ñëóæáó øàáëîíèçàòîðà â NewsletterManager, ÷òîáû ìîæíî áûëî ñîçäàâàòü êîíòåíò
    ýëåêòðîííûõ ïèñåì èç øàáëîíîâ:
    namespace Acme\HelloBundle\Newsletter;
    use Symfony\Component\Templating\EngineInterface;
    class NewsletterManager
    {
    protected $mailer;
    protected $templating;
    public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
    {
    $this->mailer = $mailer;
    $this->templating = $templating;
    }
    }

    // ...

    Íàñòðîéêó êîíòåéíåðà âûïîëíèòü íå ñëîæíî:
    ˆ

    2.15.

    YAML

    Êîíòåéíåð ñëóæá

    299

    Symfony Documentation, Âûïóñê 2.0

    services:
    newsletter_manager:
    class:
    %newsletter_manager.class%
    arguments: [@mailer, @templating]

    ˆ

    XML






    ˆ

    PHP

    $container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(
    new Reference('mailer'),
    new Reference('templating')
    )
    ));

    Ñëóæáà newsletter_manager òåïåðü èìååò äîñòóï ê ñëóæáàì mailer è templating. Ýòî
    òèïè÷íûé ïóòü ïî ñîçäàíèþ ñëóæá, ñïåöèôè÷íûõ äëÿ âàøåãî ïðèëîæåíèÿ, êîòîðûé
    ïîçâîëÿåò èñïîëüçîâàòü âñþ ìîùü ðàçëè÷íûõ ñëóæá Ôðåéìâîðêà.

    Ñîâåò: Óäîñòîâåðüòåñü, ÷òî â êîíôèãóðàöèè âàøåãî ïðèëîæåíèÿ ïðèñóòñòâóåò ðàçäåë

    swiftmailer. Êàê óêàçàíî â ñåêöèè Èìïîðò êîíôèãóðàöèè ïðè ïîìîùè ðàñøèðåíèé
    êîíòåéíåðà , êëþ÷ swiftmailer âíåäðÿåò ðàñøèðåíèå èç SwiftmailerBundle, êîòîðîå
    ðåãèñòðèðóåò ñëóæáó mailer.

    2.15.9 Ïðîäâèíóòàÿ êîíôèãóðàöèÿ êîíòåéíåðà

    Êàê âû ìîãëè âèäåòü ðàíåå, îïðåäåëåíèå ñëóæá â êîíòåéíåðå âûïîëíÿåòñÿ ëåãêî, â îñíîâíîì ñ ïðèìåíåíèåì êëþ÷à êîíôèãóðàöèè service è íåñêîëüêèõ ïàðàìåòðîâ. Òåì íå
    ìåíåå, êîíòåéíåð èìååò åù¼ íåñêîëüêî äîïîëíèòåëüíûõ èíñòðóìåíòîâ, ñ ïîìîùüþ êîòîðûõ ìîæíî ïîëó÷èòü äîïîëíèòåëüíóþ ôóíêöèîíàëüíîñòü, ñîçäàâàòü áîëåå ñëîæíûå
    ñëóæáû è âûïîëíÿòü îïåðàöèè ïîñëå ñîçäàíèÿ êîíòåéíåðà.
    Ïóáëè÷íûå è ïðèâàòíûå ñëóæáû

    Êàê ïðàâèëî, ïðè îïðåäåëåíèè ñëóæá, âû ðàññ÷èòûâàåòå ïîëó÷èòü äîñòóï ê íèì â âàøåì ïðèëîæåíèè. Òàêèå ñëóæáû íàçûâàþòñÿ ïóáëè÷íûìè (public). Íàïðèìåð, ñëóæáà
    300

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    doctrine èç ñîñòàâà DoctrineBundle ÿâëÿåòñÿ ïóáëè÷íîé è âû ìîæåòå ïîëó÷èòü ê íåé
    äîñòóï ñëåäóþùèì îáðàçîì:
    $doctrine = $container->get('doctrine');

    Òåì íå ìåíåå, èìåþòñÿ ñëó÷àè, êîãäà âû íå çàõîòèòå, ÷òîáû âàøè ñëóæáû áûëè ïóáëè÷íûìè. Êàê ïðàâèëî, ýòî ñëó÷àåòñÿ, êîãäà ñëóæáà îïðåäåëåíà ëèøü äëÿ òîãî, ÷òîáû
    áûòü èñïîëüçîâàííîé â êà÷åñòâå àðãóìåíòà äëÿ äðóãîé ñëóæáû.

    Ïðèìå÷àíèå: Åñëè âû èñïîëüçóåòå ïðèâàòíûå ñëóæáû â êà÷åñòâå àðãóìåíòà áîëåå
    ÷åì äëÿ îäíîé ñëóæáû, â ðåçóëüòàòå áóäóò ñîçäàíû äâà ðàçëè÷íûõ ýêçåìïëÿðà ýòîé
    ïðèâàòíîé ñëóæáû (ò.å. new PrivateFooBar()).
    Ñëóæáà äîëæíà áûòü ïðèâàòíîé, êîãäà âû íå õîòèòå, ÷òîáû îíà áûëà äîñòóïíà íàïðÿìóþ èç êîäà.
    Íàïðèìåð:
    ˆ

    YAML

    services:
    foo:
    class: Acme\HelloBundle\Foo
    public: false

    ˆ

    XML



    ˆ

    PHP

    $definition = new Definition('Acme\HelloBundle\Foo');
    $definition->setPublic(false);
    $container->setDefinition('foo', $definition);

    Òåïåðü ñëóæáà îïðåäåëåíà êàê ïðèâàòíàÿ è â
    ïðÿìóþ:

    íå ìîæåòå

    ïîëó÷èòü ê íåé äîñòóï íà-

    $container->get('foo');

    Òåì íå ìåíåå, äàæå åñëè ñëóæáà îáîçíà÷åíà êàê ïðèâàòíàÿ, âû åù¼ ìîæåòå èñïîëüçîâàòü
    å¼ ïñåâäîíèì (alias, ñì. íèæå) äëÿ äîñòóïà ê íåé (ïðè ïîìîùè ýòîãî ïñåâäîíèìà).

    Ïðèìå÷àíèå: Ïî óìîë÷àíèþ âñå ñëóæáû - ïóáëè÷íûå.

    2.15.

    Êîíòåéíåð ñëóæá

    301

    Symfony Documentation, Âûïóñê 2.0

    Ïñåâäîíèìû

    Ïðè èñïîëüçîâàíèè îñíîâíûõ èëè æå ñòîðîííèõ ïàêåòîâ â âàøåì ïðèëîæåíèè, âû âîçìîæíî çàõîòèòå èñïîëüçîâàòü ÿðëû÷êè äëÿ äîñòóïà ê íåêîòîðûì èõ ñëóæáàì. Âû ìîæåòå ñäåëàòü ýòî ïðè ïîìîùè ïñåâäîíèìîâ è äàæå ìîæåòå ñîçäàâàòü ïñåâäîíèìû äëÿ
    ïðèâàòíûõ ñëóæá.
    ˆ

    YAML

    services:
    foo:
    class: Acme\HelloBundle\Foo
    bar:
    alias: foo

    ˆ

    XML




    ˆ

    PHP

    $definition = new Definition('Acme\HelloBundle\Foo');
    $container->setDefinition('foo', $definition);
    $containerBuilder->setAlias('bar', 'foo');

    Ýòî îçíà÷àåò, ÷òî ïðè èñïîëüçîâàíèè êîíòåéíåðà, âû ìîæåòå ïîëó÷èòü äîñòóï ê ñëóæáå
    foo çàïðàøèâàÿ ñëóæáó bar:
    $container->get('bar'); //  èòîãå ïîëó÷èòå ñëóæáó foo

    Ïîäêëþ÷åíèå ôàéëîâ

    Òàêæå âîçìîæíû ñëó÷àè, êîãäà âàì áóäåò íåîáõîäèìî ïîäêëþ÷èòü íåêîòîðûé ôàéë
    ïðÿìî ïåðåä çàãðóçêîé ñëóæáû. Äëÿ ýòîãî âû ìîæåòå âîñïîëüçîâàòüñÿ äèðåêòèâîé file:
    ˆ

    YAML

    services:
    foo:
    class: Acme\HelloBundle\Foo\Bar
    file: %kernel.root_dir%/src/path/to/file/foo.php

    ˆ

    302

    XML

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0


    %kernel.root_dir%/src/path/to/file/foo.php


    ˆ

    PHP

    $definition = new Definition('Acme\HelloBundle\Foo\Bar');
    $definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php');
    $container->setDefinition('foo', $definition);

    Èìåéòå â âèäó, ÷òî symfony áóäåò ïîäêëþ÷àòü ôàéë ïðè ïîìîùè PHP ôóíêöèè
    require_once, ïîýòîìó ôàéë áóäåò ïîäêëþ÷àòüñÿ îäèí åäèíñòâåííûé ðàç â ðàìêàõ êàæäîãî çàïðîñà.
    Òàãè (tags)

    Òî÷íî òàêæå êàê âàøà çàïèñü â áëîãå ìîæåò áûòü ñíàáæåíà òàãàìè, òàê è ëþáàÿ ñëóæáà
    ìîæåò èìåòü ñâîè òàãè.  êîíòåéíåðå ñëóæá òàã îçíà÷àåò, ÷òî ñëóæáà èñïîëüçóåòñÿ äëÿ
    íåêîòîðûõ ñïåöèôè÷åñêèõ ôóíêöèé. Äàâàéòå ðàññìîòðèì ïðèìåð:
    ˆ

    YAML

    services:
    foo.twig.extension:
    class: Acme\HelloBundle\Extension\FooExtension
    tags:
    - { name: twig.extension }

    ˆ

    XML





    ˆ

    PHP

    $definition = new Definition('Acme\HelloBundle\Extension\FooExtension');
    $definition->addTag('twig.extension');
    $container->setDefinition('foo.twig.extension', $definition);

    Òàã twig.extension - ýòî ñïåöèàëèçèðîâàííûé òàã, êîòîðûé TwigBundle èñïîëüçóåò
    âî âðåìÿ êîíôèãóðèðîâàíèÿ. Ïðèñâàèâàÿ ñëóæáå òàã twig.extension, TwigBundle áóäåò çíàòü, ÷òî ñëóæáà foo.twig.extension äîëæíà áûòü çàðåãèñòðèðîâàíà â êà÷åñòâå
    ðàñøèðåíèÿ Twig. Äðóãèìè ñëîâàìè, Twig òàêèì îáðàçîì èùåò âñå ñëóæáû ñ òàãîì
    twig.extension è àâòîìàòè÷åñêè ðåãèñòðèðóåò èõ êàê ðàñøèðåíèÿ Twig.

    2.15.

    Êîíòåéíåð ñëóæá

    303

    Symfony Documentation, Âûïóñê 2.0

    Òàãè, òàêèì îáðàçîì, ÿâëÿþòñÿ ñïîñîáîì ñîîáùèòü Symfony2 èëè äðóãèì ñòîðîííèì
    ïàêåòàì, ÷òî âàøà ñëóæáà äîëæíà áûòü çàðåãèñòðèðîâàíà èëè èñïîëüçîâàíà íåêîòîðûì
    îñîáûì ñïîñîáîì âíóòðè öåëåâîãî ïàêåòà.
    Íèæå ïðåäñòàâëåí ñïèñîê òàãîâ, äîñòóïíûõ â ÿäðå Symfony2. Êàæäûé èç ýòèõ òàãîâ
    èìååò ñâîé ñîáñòâåííûé ýôôåêò íà âàøó ñëóæáó è ìíîãèå òàãè òðåáóþò íàëè÷èÿ äîïîëíèòåëüíûõ ïàðàìåòðîâ (ïîìèìî ïàðàìåòðà name).
    ˆ assetic.lter
    ˆ assetic.templating.php
    ˆ data_collector
    ˆ form.eld_factory.guesser
    ˆ kernel.cache_warmer
    ˆ kernel.event_listener
    ˆ monolog.logger
    ˆ routing.loader
    ˆ security.listener.factory
    ˆ security.voter
    ˆ templating.helper
    ˆ twig.extension
    ˆ translation.loader
    ˆ validator.constraint_validator
    2.15.10 Äîïîëíèòåëüíî ÷èòàéòå â êíèãå ðåöåïòîâ:

    ˆ /cookbook/service_container/factories
    ˆ /cookbook/service_container/parentservices
    ˆ

    Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ

    2.16 Ñîñòàâíûå ÷àñòè
    Ïîõîæå, ÷òî âû õîòèòå ïîíÿòü, êàê ðàáîòàåò Symfony2 è êàê åãî ðàñøèðèòü. Ýòî ðàäóåò!
    Ýòîò ðàçäåë ïîäðîáíî îáúÿñíÿåò âíóòðåííîñòè Symfony2.

    304

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: ×òåíèå ýòîãî ðàçäåëà íåîáõîäèìî, òîëüêî åñëè âû õîòèòå ïîíÿòü, êàê

    ðàáîòàåò Symfony2 çà êóëèñàìè èëè åñëè õîòèòå ðàñøèðÿòü Symfony2.

    2.16.1 Îáçîð

    Êîä Symfony2 ñäåëàí èç íåñêîëüêèõ íåçàâèñèìûõ ñëî¼â. Êàæäûé ñëåäóþùèé ñëîé íàäñòðàèâàåòñÿ íà ïðåäûäóùåì.

    Ñîâåò:

    íåïîñðåäñòâåííî
    ôðåéìñ
    ïîìîùüþ
    êëàññà
    Symfony\Component\ClassLoader\UniversalClassLoader è ôàéëà src/autoload.php.
    Çà äîïîëíèòåëüíîé èíôîðìàöèåé îáðàùàéòåñü ê ðàçäåëó , ïîñâÿù¼ííîìó ýòîé òåìå.
    âîðêîì;

    Êîìïîíåíò

    Àâòîçàãðóçêà
    íå
    îíà
    âûïîëíÿåòñÿ

    óïðàâëÿåòñÿ
    íåçàâèñèìî

    HttpFoundation

    Íà ñàìîì ãëóáîêîì óðîâíå íàõîäèòñÿ êîìïîíåíò :namespace:`Symfony\\Component\\HttpFound
    HttpFoundation ïðåäîñòàâëÿåò îñíîâíûå îáúåêòû, íåîáõîäèìûå äëÿ ðàáîòû ñ HTTP.
    Ýòî îáúåêòíî-îðèåíòèðîâàííàÿ àáñòðàêöèÿ íåêîòîðûõ âñòðîåííûõ PHP ôóíêöèé è
    ïåðåìåííûõ:
    ˆ Êëàññ Symfony\Component\HttpFoundation\Request àáñòðàãèðóåò îñíîâíûå ãëîáàëüíûå ïåðåìåííûå â PHP, òàêèå êàê $_GET, $_POST, $_COOKIE, $_FILES è
    $_SERVER;
    ˆ Êëàññ Symfony\Component\HttpFoundation\Response àáñòðàãèðóåò íåêîòîðûå
    PHP ôóíêöèè òèïà header(), setcookie() è echo;

    Symfony\Component\HttpFoundation\Session
    è
    Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface
    àáñòðàãèðóþò ôóíêöèè session_*() äëÿ óïðàâëåíèÿ ñåññèåé.

    ˆ Êëàññ

    Êîìïîíåíò

    HttpKernel

    Ïîâåðõ HttpFoundation ðàñïîëàãàåòñÿ êîìïîíåíò :namespace:`Symfony\\Component\\HttpKern
    HttpKernel óïðàâëÿåò äèíàìè÷åñêîé ÷àñòüþ HTTP; ýòî òîíêàÿ îá¼ðòêà ïîâåðõ êëàññîâ
    Request è Response, êîòîðàÿ ïðèâîäèò ñïîñîáû îáðàáîòêè çàïðîñîâ ê ñòàíäàðòó.
    Êîìïîíåíò òàêæå ïðåäîñòàâëÿåò òî÷êè äëÿ ðàñøèðåíèé è èíñòðóìåíòû, äåëàþùèå åãî
    èäåàëüíîé ñòàðòîâîé ïëîùàäêîé äëÿ ñîçäàíèÿ Web ôðåéìâîðêà áåç ëèøíèõ ïðîáëåì.
    Òàêæå, äîïîëíèòåëüíî, îí äîáàâëÿåò íàñòðàèâàåìîñòü è ðàñøèðÿåìîñòü áëàãîäàðÿ êîìïîíåíòó Dependency Injection è ìîùíîé ñèñòåìå ïàêåòîâ (Bundles).

    Ñì.òàêæå:
    2.16.

    Ñîñòàâíûå ÷àñòè

    305

    Symfony Documentation, Âûïóñê 2.0

    Óçíàéòå áîëüøå î êîìïîíåíòå HttpKernel. Óçíàéòå áîëüøå î
    Ïàêåòàõ.
    Ïàêåò

    Dependency Injection

    è

    FrameworkBundle

    :namespace:`Symfony\\Bundle\\FrameworkBundle` ýòî ïàêåò, ñâÿçûâàþùèé îñíîâíûå êîìïîíåíòû è áèáëèîòåêè âìåñòå, ÷òî ñîçäà¼ò ë¼ãêèé è áûñòðûé MVC ôðåéìâîðê. Îí ïîñòàâëÿåòñÿ ñ ïðàâèëüíîé ïåðâîíà÷àëüíîé êîíôèãóðàöèåé è ñîãëàøåíèÿìè
    äëÿ îáëåã÷åíèÿ èçó÷åíèÿ.
    2.16.2 ßäðî (Kernel)

    Êëàññ Symfony\Component\HttpKernel\HttpKernel - ýòî öåíòðàëüíûé êëàññ â
    Symfony2 è îí â îòâåòå çà îáðàáîòêó êëèåíòñêèõ çàïðîñîâ. Åãî ãëàâíàÿ
    öåëü - ïðåâðàòèòü îáúåêò Symfony\Component\HttpFoundation\Request â îáúåêò
    Symfony\Component\HttpFoundation\Response.
    Êàæäûé Symfony2 Kernel íàñëåäóåò Symfony\Component\HttpKernel\HttpKernelInterface:
    function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)

    Êîíòðîëëåðû (Controllers)

    Ïðè ïðåîáðàçîâàíèÿ çàïðîñà â îòâåò, Kernel ïîëàãàåòñÿ íà Controller. Êîíòðîëëåð ìîæåò áûòü ëþáîé âàëèäíîé PHP-ñóùíîñòüþ, êîòîðóþ ìîæíî âûçâàòü òåì èëè èíûì
    îáðàçîì.
    ßäðî äåëåãèðóåò ïðàâî âûáîðà çàïóñòèòü òîò èëè èíîé êîíòðîëëåð êëàññó, ðåàëèçóþùåìó èíòåðôåéñ Symfony\Component\HttpKernel\Controller\ControllerResolverInterface:
    public function getController(Request $request);
    public function getArguments(Request $request, $controller);

    Ìåòîä :method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterfa
    âîçâðàùàåò
    êîíòðîëëåð
    (PHP
    callable
    ôóíêöèþ,
    ìåòîä,
    çàìûêàíèå...), àññîöèèðîâàííûé ñ äàííûì çàïðîñîì. Êàíîíè÷åñêàÿ ðåàëèçàöèÿ
    (Symfony\Component\HttpKernel\Controller\ControllerResolver) èùåò àòðèáóò çàïðîñà _controller, êîòîðûé õðàíèò íàèìåíîâàíèå êîíòðîëëåðà (ñòðîêó class::method,
    íàïðèìåð Bundle\BlogBundle\PostController:indexAction).

    Ñîâåò: Ðåàëèçàöèÿ ïî óìîë÷àíèþ èñïîëüçóåò Symfony\Bundle\FrameworkBundle\EventListener\Rou
    äëÿ îïðåäåëåíèÿ àòðèáóòà _controller èç çàïðîñà (see

    306

    Ñîáûòèå kernel.request ).

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Ìåòîä :method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterfa
    âîçâðàùàåò ìàññèâ àðãóìåíòîâ äëÿ ïåðåäà÷è èõ â êîíòðîëëåð. Ðåàëèçàöèÿ ïî óìîë÷àíèþ àâòîìàòè÷åñêè îïðåäåëÿåò àðãóìåíòû, îñíîâûâàÿñü íà àòðèáóòàõ çàïðîñà.

    Ñîïîñòàâëåíèå àðãóìåíòîâ ìåòîäà êîíòðîëëåðà ïî àòðèáóòàì çàïðîñà
    Äëÿ êàæäîãî àðãóìåíòà ìåòîäà Symfony2 ïûòàåòñÿ ïîëó÷èòü èç çàïðîñà çíà÷åíèå
    àòðèáóòà ñ òàêèì æå èìåíåì. Åñëè îí íå îïðåäåë¼í, èñïîëüçóåòñÿ çíà÷åíèå ïî óìîë÷àíèþ (åñëè îíî òàêæå îïðåäåëåíî):
    // Symfony2 áóäåò èñêàòü îáÿçàòåëüíûé àòðèáóò 'id'
    // è îïöèîíàëüíûé àòðèáóò 'admin'
    public function showAction($id, $admin = true)
    {
    // ...
    }

    Îáðàáîòêà çàïðîñîâ

    Ìåòîä handle() ïðèíèìàåò Request è âñåãäà âîçâðàùàåò Response. Ïðè êîíâåðòàöèè
    îáúåêòà Request, handle() ïîëàãàåòñÿ íà Resolver è óïîðÿäî÷åííóþ öåïü íîòèôèêàöèé î
    ñîáûòèÿõ (Event notications, ñì. ñëåäóþùóþ ñåêöèþ äëÿ áîëåå ïîäðîáíîé èíôîðìàöèè
    î êàæäîì ñîáûòèè èç ýòîé öåïè):
    1. Ïåðåä

    òåì

    êàê

    ÷òî-ëèáî äåëàòü, ñðàáàòûâàåò íîòèôèêàöèÿ î ñîáûòèè
    kernel.request  åñëè îäèí èç ñëóøàòåëåé (listeners) âîçâðàùàåò îáúåêò
    Response, ïðîöåññ ñðàçó ïåðåõîäèò ê øàãó 8;

    2. Âûçûâàåòñÿ Resolver äëÿ îïðåäåëåíèÿ Êîíòðîëëåðà, êîòîðûé íåîáõîäèìî âûïîëíèòü;
    3. Ñëóøàòåëè ñîáûòèÿ kernel.controller òåïåðü ìîãóò ìàíèïóëèðîâàòü ìåòîäîì
    Êîíòðîëëåðà (èçìåíèòü, îáåðíóòü...);
    4. Kernel ïðîâåðÿåò, ÷òî Êîíòðîëëåð ïðåäñòàâëÿåò ñîáîé âàëèäíûé PHP callable;
    5. Äëÿ îïðåäåëåíèÿ àðãóìåíòîâ Êîíòðîëëåðà âûçûâàåòñÿ Resolver;
    6. Kernel âûïîëíÿåò Êîíòðîëëåð;
    Êîíòðîëëåð íå âîçâðàùàåò îáúåêò Response, ñëóøàòåëè ñîáûòèÿ
    kernel.view ìîãóò êîíâåðòèðîâàòü äàííûå, êîòîðûå âåðíóë Êîíòðîëëåð â îáúåêò Response;

    7. Åñëè

    8. Ñëóøàòåëè ñîáûòèÿ kernel.response ìîãóò ìàíèïóëèðîâàòü îáúåêòîì Response
    ( êîíòåíò è çàãîëîâêè);
    9. Âîçâðàùàåòñÿ Îòâåò.
    2.16.

    Ñîñòàâíûå ÷àñòè

    307

    Symfony Documentation, Âûïóñê 2.0

    Åñëè âî âðåìÿ ýòîãî ïðîöåññà âîçíèêàåò èñêëþ÷èòåëüíàÿ ñèòóàöèÿ, ñðàáàòûâàåò ñîáûòèå kernel.exception è åãî ñëóøàòåëè ïîëó÷àþò âîçìîæíîñòü êîíâåðòèðîâàòü èñêëþ÷åíèå (Exception) â Îòâåò. Åñëè ýòî óäà¼òñÿ, ñîáûòèå óâåäîìëÿåòñÿ, åñëè íåò, èñêëþ÷åíèå âûçûâàåòñÿ ïîâòîðíî.
    Åñëè âû íå õîòèòå, ÷òîáû âîçíèêàëè èñêëþ÷åíèÿ (äëÿ âëîæåííûõ çàïðîñîâ, ê ïðèìåðó),
    îòêëþ÷èòå ñîáûòèå kernel.exception ïåðåäàâ false â êà÷åñòâå òðåòüåãî àðãóìåíòà
    ìåòîäà handle().
    Âíóòðåííèå Çàïðîñû

     ëþáîé ìîìåíò âî âðåìÿ îáðàáîòêè çàïðîñà (íàçîâ¼ì åãî `ìàñòåð'), ìîæåò áûòü îáðàáîòàí ïîäçàïðîñ. Âû ìîæåòå ïåðåäàòü òèï çàïðîñà â ìåòîä handle() åãî âòîðûì
    ïàðàìåòðîì:
    ˆ HttpKernelInterface::MASTER_REQUEST;
    ˆ HttpKernelInterface::SUB_REQUEST.
    Òèï òàêæå ïåðåäà¼òñÿ âî âñå ñîáûòèÿ, è èõ ñëóøàòåëè ìîãóò äåéñòâîâàòü â ñîîòâåòñòâèè ñ ïåðåäàííûì òèïîì (íåêîòîðûå äåéñòâèÿ ìîãóò ñîîòâåòñòâîâàòü òîëüêî ìàñòåðçàïðîñó).
    Ñîáûòèÿ

    Êàæäîå

    ñîáûòèå,

    ñîçäàâàåìîå

    â

    Kernel,
    ýòî
    äî÷åðíèé
    Symfony\Component\HttpKernel\Event\KernelEvent. Ýòî îçíà÷àåò, ÷òî
    ñîáûòèå èìååò äîñòóï ê îäíîé è òîé æå áàçîâîé èíôîðìàöèè:

    êëàññ
    êàæäîå

    ˆ getRequestType() - âîçâðàùàåò òèï çàïðîñà (HttpKernelInterface::MASTER_REQUEST
    èëè HttpKernelInterface::SUB_REQUEST);
    ˆ getKernel() - âîçâðàùàåò ýêçåìïëÿð Kernel, îáðàáàòûâàþùèé ýòîò çàïðîñ;
    ˆ getRequest() - âîçâðàùàåò îáúåêò Request, ñîîòâåòñòâóþùèé îáðàáàòûâàåìîìó
    çàïðîñó;
    getRequestType()

    Ìåòîä getRequestType() ïîçâîëÿåò ñëóøàòåëÿì óçíàâàòü òèï çàïðîñà. Íàïðèìåð, åñëè
    ñëóøàòåëü äîëæåí áûòü àêòèâåí òîëüêî äëÿ ìàñòåð-çàïðîñà, äîáàâüòå ñëåäóþùèé êîä
    â íà÷àëî âàøåãî ñëóøàþùåãî ìåòîäà:
    use Symfony\Component\HttpKernel\HttpKernelInterface;

    308

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
    // íåìåäëåííî âîçâðàùàåìñÿ
    return;
    }

    Ñîâåò: Åñëè âû åù¼ íå çíàêîìû ñ Äèñïåò÷åðîì Ñîáûòèé Symfony2 (Event Dispatcher),
    ïðî÷èòàéòå ñíà÷àëà ñåêöèþ

    Ñîáûòèÿ .

    Ñîáûòèå kernel.request
    Êëàññ ñîáûòèÿ :

    Symfony\Component\HttpKernel\Event\GetResponseEvent

    Öåëü ýòîãî ñîáûòèÿ - ëèáî íåçàìåäëèòåëüíî âåðíóòü îáúåêò Response, èëè æå ïîäãîòîâèòü ïåðåìåííûå, ÷òîáû ìîæíî áûëî âûçâàòü êîíòðîëëåð ïîñëå ñîáûòèÿ. Ëþáîé ñëóøàòåëü ìîæåò âåðíóòü îáúåêò Response ïðè ïîìîùè ìåòîäà ñîáûòèÿ setResponse(). Â
    ýòîì ñëó÷àå, âñå îñòàëüíûå ñëóøàòåëè íå áóäóò âûçûâàòüñÿ.
    Ýòî
    ñîáûòèå
    èñïîëüçóåòñÿ
    â
    FrameworkBundle
    äëÿ
    çàïîëíåíèÿ
    àòðèáóòà
    _controller
    â
    îáúåêòå
    Request
    ïðè
    ïîìîùè
    êëàññà
    Symfony\Bundle\FrameworkBundle\EventListener\RouterListener.
    RequestListener
    èñïîëüçóåò
    îáúåêò,
    ðåàëèçóþùèé
    èíòåðôåéñ
    Symfony\Component\Routing\RouterInterface äëÿ ñîãëàñîâàíèÿ îáúåêòà Request
    è îïðåäåëåíèÿ íàèìåíîâàíèÿ Êîíòðîëëåðà (êîòîðîå õðàíèòñÿ â àòðèáóòå _controller
    îáúåêòà Request).

    Ñîáûòèå kernel.controller
    Êëàññ ñîáûòèÿ :

    Symfony\Component\HttpKernel\Event\FilterControllerEvent

    Ýòî ñîáûòèå íå èñïîëüçóåòñÿ â FrameworkBundle, íî îíî ìîæåò áûòü òî÷êîé âõîäà,
    èñïîëüçóåìîé äëÿ ìîäèôèêàöèè èñïîëíÿåìîãî êîíòðîëëåðà:
    use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
    public function onKernelController(FilterControllerEvent $event)
    {
    $controller = $event->getController();
    // ...

    }

    // the controller can be changed to any PHP callable
    $event->setController($controller);

    2.16.

    Ñîñòàâíûå ÷àñòè

    309

    Symfony Documentation, Âûïóñê 2.0

    Ñîáûòèå kernel.view
    Êëàññ ñîáûòèÿ :

    Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent

    Ýòî ñîáûòèå íå èñïîëüçóåòñÿ â FrameworkBundle, íî îíî ìîæåò áûòü èñïîëüçîâàíî äëÿ
    ðåàëèçàöèè ïîäñèñòåìû view. Ýòî ñîáûòèå âûçûâàåòñÿ òîëüêî åñëè Êîíòðîëëåð íå âîçâðàùàåò îáúåêò Response. Íàçíà÷åíèå ýòîãî ñîáûòèÿ - ðàçðåøèòü êîíâåðòàöèþ âîçâðàùàåìûõ çíà÷åíèé â îáúåêò Response.
    Çíà÷åíèå,

    âîçâðàùàåìîå
    getControllerResult:

    Êîíòðîëëåðîì

    äîñòóïíî

    ïðè

    ïîìîùè

    ìåòîäà

    use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
    use Symfony\Component\HttpFoundation\Response;
    public function onKernelView(GetResponseForControllerResultEvent $event)
    {
    $val = $event->getReturnValue();
    $response = new Response();
    // êîä ïîëó÷åíèÿ îáúåêòà Response èç ïîëó÷åííîãî çíà÷åíèÿ
    $event->setResponse($response);

    }

    Ñîáûòèå kernel.response
    Êëàññ ñîáûòèÿ :

    Symfony\Component\HttpKernel\Event\FilterResponseEvent

    Íàçíà÷åíèå ýòîãî ñîáûòèÿ - ïîçâîëèòü äðóãèì ñèñòåìàì ìîäèôèöèðîâàòü èëè çàìåíÿòü
    îáúåêò Response ïîñëå åãî ñîçäàíèÿ:
    public function onKernelResponse(FilterResponseEvent $event)
    {
    $response = $event->getResponse();
    // .. modify the response object
    }

    FrameworkBundle ðåãèñòðèðóåò íåñêîëüêî ñëóøàòåëåé:
    ˆ Symfony\Component\HttpKernel\EventListener\ProfilerListener:
    äàííûå äëÿ òåêóùåãî çàïðîñà;

    ñîáèðàåò

    ˆ Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener:
    âíåäðÿåò Web Debug Toolbar;

    310

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ Symfony\Component\HttpKernel\EventListener\ResponseListener: óñòàíàâëèâàåò Content-Type îòâåòà, îñíîâûâàÿñü íà ôîðìàòå çàïðîñà;
    ˆ Symfony\Component\HttpKernel\EventListener\EsiListener: äîáàâëÿåò çàãîëîâîê Surrogate-Control, â ñëó÷àå åñëè îòâåò íåîáõîäèìî ïàðñèòü íà ïðåäìåò íàëè÷èÿ ESI òàãîâ.

    Ñîáûòèå kernel.exception
    Êëàññ ñîáûòèÿ :

    Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent

    FrameworkBundle ðåãèñòðèðóåò Symfony\Component\HttpKernel\EventListener\ExceptionListener,
    êîòîðûé ïåðåíàïðàâëÿåò Request â óêàçàííûå Êîíòðîëëåð (îïðåäåëÿåòñÿ çíà÷åíèåì
    ïàðàìåòðà exception_listener.controller, óêàçûâàåòñÿ â íîòàöèè class::method).
    Ñëóøàòåëü ýòîãî ñîáûòèÿ ìîæåò ñîçäàâàòü îáúåêò Response, ñîçäàâàòü íîâûé îáúåêò
    Exception èëè æå íè÷åãî íå äåëàòü:
    use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
    use Symfony\Component\HttpFoundation\Response;
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
    $exception = $event->getException();
    $response = new Response();
    // Íàñòðàèâàåì îáúåêò Response, îñíîâûâàÿñü íà ïåðåõâà÷åííîì èñêëþ÷åíèè
    $event->setResponse($response);

    }

    // êàê âàðèàíò - âû ìîæåòå ñîçäàòü íîâîå èñêëþ÷åíèå
    // $exception = new \Exception('Some special exception');
    // $event->setException($exception);

    2.16.3 Äèñïåò÷åð ñîáûòèé (Event Dispatcher)

    Îáúåêòíî-îðèåíòèðîâàííûé êîä ïðîø¼ë äëèííûé ïóòü ïî îáåñïå÷åíèþ ðàñøèðÿåìîñòè
    êîäà. Ïóò¼ì ñîçäàíèÿ óçêîñïåöèàëèçèðîâàííûõ êëàññîâ, âàø êîä ñòàíîâèòñÿ áîëåå ãèáêèì è ðàçðàáîò÷èê ìîæåò ðàñøèðÿòü åãî ïðè ïîìîùè äî÷åðíèõ êëàññîâ, ÷òîáû èçìåíÿòü
    èõ ïîâåäåíèå. Íî ÷òî, åñëè òðåáóåòñÿ èñïîëüçîâàòü åãî èçìåíåíèÿ ñîâìåñòíî ñ äðóãèìè
    ðàçðàáîò÷èêàìè, êîòîðûå òàêæå ñîçäàþò ñâîè äî÷åðíèå êëàññû? Çäåñü èñïîëüçîâàíèå
    íàñëåäîâàíèÿ óæå íå ñòîëü óäîáíî.
    Ðàññìîòðèì ðåàëüíûé ïðèìåð, â êîòîðîì âàì íóæíî ñîçäàòü ñèñòåìó ïëàãèíîâ äëÿ âàøåãî ïðîåêòà. Ïëàãèí äîëæåí èìåòü âîçìîæíîñòü äîáàâëÿòü ìåòîäû èëè æå äåëàòü

    2.16.

    Ñîñòàâíûå ÷àñòè

    311

    Symfony Documentation, Âûïóñê 2.0

    ÷òî-òî äî èëè ïîñëå âûïîëíåíèÿ íåêîòîðîãî ìåòîäà, íå ïåðåñåêàÿñü ñ ïðî÷èìè ïëàãèíàìè. Ýòó çàäà÷ó íåïðîñòî ðåøèòü ïðè ïîìîùè îäèíî÷íîãî íàñëåäîâàíèÿ, äà è ìíîæåñòâåííîå íàñëåäîâàíèå (åñëè áû îíî áûëî âîçìîæíî â PHP) èìååò ñâîè íåäîñòàòêè.
    Äèñïåò÷åð ñîáûòèé Symfony2 ðåàëèçóåò øàáëîí ïðîåêòèðîâàíèÿ Observer ïðîñòûì è
    ýôôåêòèâíûì ñïîñîáîì, ïîçâîëÿÿ ñîçäàâàòü, íàïðèìåð, ÷òî-òî âðîäå ñèñòåìû ïëàãèíîâ,
    êîòîðóþ óïîìèíàëè âûøå, è äåëàÿ âàø ïðîåêò äåéñòâèòåëüíî ðàñøèðÿåìûì.
    Ðàññìîòðèì åù¼ îäèí ïðîñòîé ïðèìåð èç Symfony2 HttpKernel component. Êîãäà ñîçäà¼òñÿ îáúåêò Response, áûëî áû çäîðîâî ïîçâîëèòü äðóãèì ñèñòåìàì ïðîåêòà ìîäèôèöèðîâàòü åãî (íàïðèìåð, äîáàâèòü çàãîëîâêè äëÿ êýøèðîâàíèÿ) ïåðåä ïîñëåäóþùèì èñïîëüçîâàíèåì. Äëÿ òîãî, ÷òîáû äîñòè÷ü ýòîãî, ÿäðî Symfony2 ñîçäà¼ò ñîáûòèå kernel.response. Âîò êàê ýòî ðàáîòàåò:
    ˆ

    (listener, PHP îáúåêò) ñîîáùàåò öåíòðàëüíîìó
    ñîáèðàåòñÿ ñëóøàòü (îæèäàòü) ñîáûòèå kernel.response;
    Ñëóøàòåëü

    äèñïåò÷åðó,

    ÷òî îí

    ˆ  êàêîé-òî ìîìåíò ÿäðî Symfony2 ïðîñèò îáúåêò äèñïåò÷åðà îòïðàâèòü ñîáûòèå
    kernel.response, è âìåñòå ñ íèì - îáúåêò Response;
    ˆ Äèñïåò÷åð óâåäîìëÿåò (ôàêòè÷åñêè âûçûâàåò ìåòîä) âñåõ ñëóøàòåëåé ñîáûòèÿ
    kernel.response, ïîçâîëÿÿ êàæäîìó èç íèõ âûïîëíèòü ìîäèôèêàöèþ îáúåêòà
    Response.
    Ñîáûòèÿ

    Êîãäà ñîîáùåíèå îòïðàâëåíî, îíî èäåíòèôèöèðóåòñÿ ïî óíèêàëüíîìó èìåíè (íàïðèìåð,
    kernel.response), êîòîðîå ìîãóò îæèäàòü íåêîòîðîå ÷èñëî ñëóøàòåëåé. Òàêæå ñîçäà¼òñÿ ýêçåìïëÿð êëàññà Symfony\Component\EventDispatcher\Event, êîòîðûé çàòåì ïåðåäà¼òñÿ âñåì ñëóøàòåëÿì. Êàê âû óâèäèòå ÷óòü ïîçæå, îáúåêò Event ÷àñòî ñîäåðæèò
    äàííûå î íàïðàâëÿåìîì ñîáûòèè.

    Ñîãëàøåíèÿ ïî èìåíîâàíèþ
    Óíèêàëüíûì èìåíåì äëÿ ñîáûòèÿ ìîæåò áûòü ëþáàÿ ñòðîêà, íî æåëàòåëüíî ñëåäîâàíèå
    íåñêîëüêèì ïðîñòûì ïðàâèëàì:
    ˆ Äîïóñòèìûå ñèìâîëû: áóêâû â íèæíåì ðåãèñòðå, öèôðû, òî÷êà (.), ïîä÷åðê (_);
    ˆ Äîáàâëÿéòå ïðåôèêñ ïðîñòðàíñòâà èì¼í ñ òî÷êîé íà êîíöå (íàïðèìåð, kernel.);
    ˆ Îêàí÷èâàéòå èìÿ ãëàãîëîì, êîòîðûé îáîçíà÷àåò äåéñòâèå (íàïðèìåð, request).
    Âîò ïàðà ïðèìåðîâ õîðîøèõ èì¼í äëÿ ñîáûòèé:
    ˆ kernel.response
    ˆ form.pre_set_data

    312

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Îáúåêòû ñîáûòèé
    Êîãäà äèñïåò÷åð óâåäîìëÿåò ñëóøàòåëåé, îí ïåðåäà¼ò èì îáúåêò Event. Áàçîâûé
    êëàññ Event î÷åíü ïðîñò: îí ñîäåðæèò ìåòîä äëÿ ïðåêðàùåíèÿ âîñïðîèçâåäåíèÿ (event
    propagation ) è íè÷åãî áîëåå.
    Çà÷àñòóþ, íåîáõîäèìî ïåðåäàâàòü â îáúåêòå Event òàêæå äàííûå î ñîáûòèè, ÷òîáû ñëóøàòåëè ìîãëè èõ îáðàáîòàòü òåì èëè èíûì îáðàçîì.  ñëó÷àå ñîáûòèÿ
    kernel.response, îáúåêò Event, ïåðåäàâàåìûé êàæäîìó ñëóøàòåëþ, ôàêòè÷åñêè èìååò òèï Symfony\Component\HttpKernel\Event\FilterResponseEvent, äî÷åðíèé ïî îòíîøåíèþ ê Event êëàññ. Ýòîò êëàññ ñîäåðæèò ìåòîäû, òàêèå êàê getResponse è
    setResponse, ïîçâîëÿþùèå ñëóøàòåëÿì ïîëó÷àòü è äàæå çàìåíÿòü îáúåêò Response.
    Ìîðàëü ýòîé èñòîðèè â ñëåäóþùåì: ïðè ñîçäàíèè ñëóøàòåëÿ íåêîòîðîãî ñîáûòèÿ, îáúåêò Event, êîòîðûé áóäåò ïåðåäàí ýòîìó ñëóøàòåëþ, ìîæåò áûòü ñïåöèàëèçèðîâàííûì
    äî÷åðíèì êëàññîì è èìåòü äîïîëíèòåëüíûå ìåòîäû äëÿ ïîëó÷åíèÿ äàííûõ ñîáûòèÿ è
    èõ îáðàáîòêè.
    Äèñïåò÷åð

    Äèñïåò÷åð - ýòî öåíòðàëüíûé îáúåêò ñèñòåìû îáðàáîòêè ñîáûòèé. Êàê ïðàâèëî, ñîçäà¼òñÿ åäèíñòâåííûé äèñïåò÷åð, êîòîðûé îáñëóæèâàåò ðååñòð ñëóøàòåëåé. Êîãäà ñîáûòèå
    ïîñòóïàåò ê äèñïåò÷åðó - îí óâåäîìëÿåò âñåõ ñëóøàòåëåé, ïîäïèñàííûõ íà ýòî ñîáûòèå.
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();

    Ïîäêëþ÷àåì Ñëóøàòåëåé

    Äëÿ òîãî, ÷òîáû îòðåàãèðîâàòü íà íåêîå ñóùåñòâóþùåå ñîáûòèå, âàì íåîáõîäèìî ïîäêëþ÷èòü ñëóøàòåëÿ ê äèñïåò÷åðó, ÷òîáû ïîñëåäíèé èìåë âîçìîæíîñòü ñîîáùèòü î ïîÿâëåíèè íóæíîãî ñîáûòèÿ. Âûçîâ ìåòîäà äèñïåò÷åðà addListener() àññîöèèðóåò ëþáóþ
    èñïîëíèìóþ ôóíêöèþ/ìåòîä ñ ñîáûòèåì:
    $listener = new AcmeListener();
    $dispatcher->addListener('foo.action', array($listener, 'onFooAction'));

    Ìåòîä addListener() ïîëó÷àåò òðè àðãóìåíòà:
    ˆ Íàèìåíîâàíèå ñîáûòèÿ, êîòîðîå ñëóøàòåëü áóäåò îæèäàòü;
    ˆ Íåêèé îáúåêò (ôóíêöèþ, â îáùåì æå ñëó÷àå PHP callable), êîòîðûé áóäåò âûçâàí
    ïðè íàñòóïëåíèè ñîáûòèÿ;
    2.16.

    Ñîñòàâíûå ÷àñòè

    313

    Symfony Documentation, Âûïóñê 2.0

    ˆ Îïöèîíàëüíûé ïðèîðèòåò (÷åì áîëüøå - òåì áîëåå âàæíûé), êîòîðûé îïðåäåëÿåò
    î÷åð¼äíîñòü âûçîâà ñëóøàòåëåé (ïî óìîë÷àíèþ 0). Åñëè äâà ñëóøàòåëÿ èìåþò
    îäèíàêîâûé ïðèîðèòåò, îíè âûïîëíÿþòñÿ â ïîðÿäêå èõ äîáàâëåíèÿ.

    Ïðèìå÷àíèå: PHP callable - ýòî ïåðåìåííàÿ, êîòîðàÿ ìîæåò áûòü èñïîëüçîâàíà â

    ôóíêöèè call_user_func() è âîçâðàùàåò true ïðè ïðîâåðêå ñ ïîìîùüþ ôóíêöèè
    is_callable(). Ýòî ìîæåò áûòü, â òîì ÷èñëå, è ýêçåìïëÿð çàìûêàíèÿ (\Closure), ñòðîêà ñ èìåíåì ôóíêöèè èëè ìàññèâ, ïðåäñòàâëÿþùèé ñîáîé ìåòîä îáúåêòà èëè æå ìåòîä
    êëàññà.
    Ðàíåå âû óæå âèäåëè êàê PHP îáúåêò ìîæåò áûòü çàðåãèñòðèðîâàí â êà÷åñòâå ñëóøàòåëÿ. Âû òàêæå ìîæåòå ðåãèñòðèðîâàòü Çàìûêàíèÿ (Closures) â êà÷åñòâå ñëóøàòåëåé:

    use Symfony\Component\EventDispatcher\Event;
    $dispatcher->addListener('foo.action', function (Event $event) {
    // ýòîò êîä áóäåò âûçâàí ïðè îáðàáîòêå ñîáûòèÿ foo.action
    });

    Êîãäà ñëóøàòåëü çàðåãèñòðèðîâàí äèñïåò÷åðîì, îí îæèäàåò íàñòóïëåíèÿ ñîáûòèÿ.
     ïðèìåðå âûøå, êîãäà ïîÿâëÿåòñÿ ñîáûòèå foo.action, äèñïåò÷åð âûçûâàåò ìåòîä
    AcmeListener::onFooAction è ïåðåäà¼ò îáúåêòó Event îäèí àðãóìåíò:
    use Symfony\Component\EventDispatcher\Event;
    class AcmeListener
    {
    // ...

    }

    public function onFooAction(Event $event)
    {
    // do something
    }

    Ñîâåò: Åñëè âû èñïîëüçóåòå Symfony2 MVC framework, ñëóøàòåëè ìîãóò áûòü çàðå-

    ãèñòðèðîâàíû ïðè ïîìîùè êîíôèãóðàöèè .  êà÷åñòâå áîíóñà, îáúåêò ñëóøàòåëÿ áóäåò
    ñîçäàí ëèøü êîãäà áóäåò íóæåí.
    Âî ìíîãèõ ñëó÷àÿõ, ñëóøàòåëþ ïåðåäà¼òñÿ ñïåöèàëèçèðîâàííûé äî÷åðíèé êëàññ
    Event. Ýòî äà¼ò ñëóøàòåëþ äîñòóï ê èíôîðìàöèè î ñîáûòèè. Ñâåðÿéòåñü ñ
    äîêóìåíòàöèåé èëè ðåàëèçàöèåé êàæäîãî êîíêðåòíîãî ñîáûòèÿ äëÿ îïðåäåëåíèÿ êàêîé èìåííî ýêçåìïëÿð Symfony\Component\EventDispatcher\Event áó314

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    kernel.event ïåðåäà¼ò
    Symfony\Component\HttpKernel\Event\FilterResponseEvent:
    äåò

    ïåðåäàí.

    Íàïðèìåð,

    ñîáûòèå

    ýêçåìïëÿð

    êëàññà

    use Symfony\Component\HttpKernel\Event\FilterResponseEvent
    public function onKernelResponse(FilterResponseEvent $event)
    {
    $response = $event->getResponse();
    $request = $event->getRequest();
    }

    // ...

    Ñîçäàíèå è îáðàáîòêà ñîáûòèÿ

    Â äîïîëíåíèå ê ðåãèñòðàöèè ñëóøàòåëåé äëÿ óæå ñóùåñòâóþùèõ ñîáûòèé, âû ìîæåòå
    ñîçäàâàòü è âûçûâàòü ñâîè ñîáñòâåííûå ñîáûòèÿ. Ýòî ìîæåò áûòü óäîáíî ïðè ñîçäàíèè
    ñòîðîííèõ áèáëèîòåê è åñëè âû õîòèòå ÷òîáû ðàçëè÷íûå êîìïîíåíòû âàøåé ñèñòåìû
    áûëè ãèáêèìè è íåçàâèñèìûìè.

    Ñòàòè÷åñêèé êëàññ Events
    Ïðåäïîëîæèì, âû õîòèòå ñîçäàòü íîâîå ñîáûòèå - store.order - êîòîðîå ñîçäà¼òñÿ âñÿêèé ðàç, êîãäà â âàøåì ïðèëîæåíèè ñîçäà¼òñÿ çàêàç. Äëÿ òîãî, ÷òîáû ïîääåðæèâàòü
    ïîðÿäîê â ïðèëîæåíèè, íà÷í¼ì ñ ñîçäàíèÿ êëàññà StoreEvents, êîòîðûé áóäåò îïðåäåëÿòü âàøå ñîáûòèå:
    namespace Acme\StoreBundle;
    final class StoreEvents
    {
    /**
    * Ñîáûòèå store.order ñîçäà¼òñÿ âñÿêèé ðàç, êîãäà â ñèñòåìå ñîçäà¼òñÿ çàêàç.
    *
    * Ñëóøàòåëü ïîëó÷èò ýêçåìïëÿð Acme\StoreBundle\Event\FilterOrderEvent
    *
    * @var string
    */
    const onStoreOrder = 'store.order';
    }

    Îòìåòèì òàêæå, ÷òî ýòîò êëàññ ïî ñóòè ñâîé íè÷åãî íå äåëàåò. Íàçíà÷åíèå êëàññà
    StoreEvents - öåíòðàëèçàöèÿ äàííûõ î ñîáûòèè. Ñëóøàòåëÿì ýòîãî ñîáûòèÿ áóäåò ïåðåäàâàòüñÿ ñïåöèàëèçèðîâàííûé êëàññ FilterOrderEvent.
    2.16.

    Ñîñòàâíûå ÷àñòè

    315

    Symfony Documentation, Âûïóñê 2.0

    Ñîçäàíèå îáúåêòà ñîáûòèÿ
    Ïîçäíåå, êîãäà âû áóäåòå îòïðàâëÿòü ýòî ñîáûòèå, âû ñîçäàäèòå ýêçåìïëÿð êëàññà Event è ïåðåäàäèòå ýòîò ýêçåìïëÿð âñåì ñëóøàòåëÿì ñîáûòèÿ. Åñëè âû íå
    õîòèòå ïåðåäàâàòü íèêàêîé äîïîëíèòåëüíîé èíôîðìàöèè ñëóøàòåëÿì, âû ìîæåòå èñïîëüçîâàòü êëàññ Symfony\Component\EventDispatcher\Event. Â áîëüøèíñòâå
    æå ñëó÷àåâ, âû áóäåòå ïåðåäàâàòü èíôîðìàöèþ î ñîáûòèè ñëóøàòåëÿì. Äëÿ
    ýòîãî íåîáõîäèìî ñîçäàòü íîâûé êëàññ, êîòîðûé áóäåò íàñëåäîâàòüñÿ îò êëàññà
    Symfony\Component\EventDispatcher\Event.
     ýòîì ïðèìåðå, êàæäûé ñëóøàòåëü áóäåò äîëæåí ïîëó÷èòü äîñòóï ê íåêîòîðîìó îáúåêòó Order. Ñîçäàäèì êëàññ Event, êîòîðûé ðåàëèçóåò òàêîå ïîâåäåíèå:
    namespace Acme\StoreBundle\Event;
    use Symfony\Component\EventDispatcher\Event;
    use Acme\StoreBundle\Order;
    class FilterOrderEvent extends Event
    {
    protected $order;
    public function __construct(Order $order)
    {
    $this->order = $order;
    }

    }

    public function getOrder()
    {
    return $this->order;
    }

    Êàæäûé ñëóøàòåëü òåïåðü èìååò äîñòóï ê îáúåêòó Order ïðè ïîìîùè ìåòîäà getOrder.

    Îòïðàâêà ñîáûòèÿ
    Ìåòîä :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::dispatch`
    óâåäîìëÿåò âñåõ ñëóøàòåëåé î ñîáûòèè. Îí ïðèíèìàåò äâà àðãóìåíòà: íàèìåíîâàíèå
    ñîáûòèÿ äëÿ îòïðàâêè è ýêçåìïëÿð Event äëÿ ïåðåäà÷è êàæäîìó ñëóøàòåëþ ýòîãî
    ñîáûòèÿ:
    use Acme\StoreBundle\StoreEvents;
    use Acme\StoreBundle\Order;

    316

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    use Acme\StoreBundle\Event\FilterOrderEvent;
    // çàêàç êàê-òî ñîçäà¼òñÿ èëè ïîëó÷àåòñÿ
    $order = new Order();
    // ...
    // ñîçäà¼ì FilterOrderEvent è åãî îòïðàâêà
    $event = new FilterOrderEvent($order);
    $dispatcher->dispatch(StoreEvents::onStoreOrder, $event);

    Îáúåêò FilterOrderEvent ñîçäà¼òñÿ è ïåðåäà¼òñÿ â ìåòîä dispatch. Òåïåðü, ëþáîé
    ñëóøàòåëü ñîáûòèÿ store.order áóäåò ïîëó÷àòü FilterOrderEvent è ñîîòâåòñòâåííî
    èìåòü äîñòóï ê îáúåêòó Order ïðè ïîìîùè ìåòîäà getOrder:
    // êàêîé-òî ñëóøàòåëü, ïîäïèñàííûé íà ñîáûòèå store.order ìåòîäîì onStoreOrder
    use Acme\StoreBundle\Event\FilterOrderEvent;
    public function onStoreOrder(FilterOrderEvent $event)
    {
    $order = $event->getOrder();
    // äàëåå âûïîëíÿþòñÿ êàêèå-òî äåéñòâèÿ ñ çàêàçîì
    }

    Âíóòðè îáúåêòà Äèñïåò÷åðà ñîáûòèé

    Åñëè âû âçãëÿíåòå íà êëàññ EventDispatcher, âû óâèäèòå, ÷òî ýòîò êëàññ ðàáîòàåò íå
    êàê Singleton (íåò ñòàòè÷åñêîãî ìåòîäà getInstance()). Ýòî ñäåëàíî ïðåäíàìåðåííî,
    òàê êàê âàì, âîçìîæíî, ïîòðåáóåòñÿ èìåòü íåñêîëüêî êîíêóðèðóþùèõ äèñïåò÷åðîâ â
    ðàìêàõ îäíîãî çàïðîñà. Íî ýòî òàêæå îçíà÷àåò, ÷òî âàì íóæåí ñïîñîá äëÿ ïåðåäà÷è
    äèñïåò÷åðó îáúåêòîâ, êîòîðûå íóæíî ïîäêëþ÷èòü èëè êîòîðûå íàäî óâåäîìèòü î ñîáûòèè.
    Îáùåïðèíÿòîé ïðàêòèêîé ÿâëÿåòñÿ âíåäðåíèå îáúåêòà äèñïåò÷åðà â âàøè îáúåêòû, ò.å.
    âíåäðåíèå çàâèñèìîñòè.
    Âû ìîæåòå èñïîëüçîâàòü âíåäðåíèå â êîíñòðóêòîð:
    class Foo
    {
    protected $dispatcher = null;

    }

    public function __construct(EventDispatcher $dispatcher)
    {
    $this->dispatcher = $dispatcher;
    }

    2.16.

    Ñîñòàâíûå ÷àñòè

    317

    Symfony Documentation, Âûïóñê 2.0

    Èëè æå âíåäðåíèå ÷åðåç ìåòîä (setter injection):
    class Foo
    {
    protected $dispatcher = null;

    }

    public function setEventDispatcher(EventDispatcher $dispatcher)
    {
    $this->dispatcher = $dispatcher;
    }

    Âûáîð òîãî èëè èíîãî ìåòîäà - ýòî äåëî âêóñà. Ìíîãèå ïðåäïî÷èòàþò ìåòîä ñ êîíñòðóêòîðîì, òàê êàê îáúåêòû ïîëíîñòüþ èíèöèàëèçèðóþòñÿ âî âðåìÿ ñîçäàíèÿ. Íî êîãäà ó
    âàñ èìååòñÿ äëèííûé ñïèñîê çàâèñèìîñòåé, èñïîëüçîâàòü ìåòîä-ñåòòåð ýòî òîæå âàðèàíò, îñîáåííî äëÿ îïöèîíàëüíûõ çàâèñèìîñòåé.

    Ñîâåò: Åñëè âû èñïîëüçóåòå âíåäðåíèå çàâèñèìîñòè êàê ìû äåëàëè â äâóõ ïðèìåðàõ âûøå, âû ìîæåòå èñïîëüçîâàòü Symfony2 Dependency Injection component äëÿ òîãî
    ÷òîáû óïðàâëÿòü âíåäðåíèåì ñëóæáû event_dispatcher äëÿ ýòèõ îáúåêòîâ.
    # src/Acme/HelloBundle/Resources/config/services.yml
    services:
    foo_service:
    class: Acme/HelloBundle/Foo/FooService
    arguments: [@event_dispatcher]

    Ïîäïèñêà íà ñîáûòèÿ

    Òèïè÷íûé ñïîñîá îæèäàòü âîçíèêíîâåíèå ñîáûòèÿ - çàðåãèñòðèðîâàòü ñëóøàòåëÿ ñîáûòèÿ ïðè ïîìîùè äèñïåò÷åðà. Ýòîò ñëóøàòåëü ìîæåò ñëóøàòü îäíî èëè íåñêîëüêî
    ñîáûòèé è óâåäîìëÿåòñÿ êàæäûé ðàç ïðè îòïðàâêå íóæíîãî ñîáûòèÿ.
    Àëüòåðíàòèâíûì ñïîñîáîì äëÿ îæèäàíèÿ ñîáûòèé - èñïîëüçîâàíèå ïîäïèñ÷èêà ñîáûòèÿ. Ïîäïèñ÷èê - ýòî PHP êëàññ, êîòîðûé èìååò âîçìîæíîñòü ñîîáùèòü äèñïåò÷åðó íà êàêèå ñîáûòèÿ îí ïîäïèñûâàåòñÿ. Ïîäïèñ÷èê äîëæåí ðåàëèçîâûâàòü èíòåðôåéñ Symfony\Component\EventDispatcher\EventSubscriberInterface, êîòîðûé òðåáóåò íàëè÷èå îäíîãî ñòàòè÷åñêîãî ìåòîäà getSubscribedEvents. Ðàññìîòðèì ïðèìåð
    ïîäïèñ÷èêà, êîòîðûé ïîäïèñûâàåòñÿ íà ñîáûòèÿ kernel.response è store.order:
    namespace Acme\StoreBundle\Event;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

    318

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    class StoreSubscriber implements EventSubscriberInterface
    {
    static public function getSubscribedEvents()
    {
    return array(
    'kernel.response' => 'onKernelResponse',
    'store.order'
    => 'onStoreOrder',
    );
    }
    public function onKernelResponse(FilterResponseEvent $event)
    {
    // ...
    }

    }

    public function onStoreOrder(FilterOrderEvent $event)
    {
    // ...
    }

    Ýòîò êëàññ ïîõîæ íà êëàññ ñëóøàòåëÿ, çà èñêëþ÷åíèåì òîãî, ÷òî îí ñàì ìîæåò
    ñîîáùèòü äèñïåò÷åðó, íà êàêèå èìåííî ñîáûòèÿ îí ïîäïèñûâàåòñÿ (áóäåò ñëóøàòü). Äëÿ ðåãèñòðàöèè ïîäïèñ÷èêà â äèñïåò÷åðå íåîáõîäèìî èñïîëüçîâàòü ìåòîä

    :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::addSubscriber`:
    use Acme\StoreBundle\Event\StoreSubscriber;
    $subscriber = new StoreSubscriber();
    $dispatcher->addSubscriber($subscriber);

    Äèñïåò÷åð àâòîìàòè÷åñêè çàðåãèñòðèðóåò ïîäïèñ÷èêà äëÿ êàæäîãî ñîáûòèÿ, âîçâðàùàåìîãî ìåòîäîì getSubscribedEvents. Ýòîò ìåòîä âîçâðàùàåò ìàññèâ, èíäåêñàìè êîòîðîãî ñëóæàò íàèìåíîâàíèÿ ñîáûòèé, à çíà÷åíèÿìè ñëóæàò ëèáî íàèìåíîâàíèÿ ìåòîäîâ,
    êîòîðûå áóäóò âûçâàíû, ëèáî ìàññèâû ñ èìåíåì ìåòîäà è åãî ïðèîðèòåòîì ïðè îáðàáîòêå ñîáûòèÿ.

    Ñîâåò: Åñëè âû èñïîëüçóåòå Symfony2 MVC framework, ïîäïèñ÷èêè ìîæíî ðåãèñòðè-

    ðîâàòü ïðè ïîìîùè êîíôèãóðàöèè .  êà÷åñòâå ïðèÿòíîãî áîíóñà, ýêçåìïëÿð ïîäïèñ÷èêà
    áóäåò ñîçäàí ëèøü êîãäà áóäåò íóæåí.

    2.16.

    Ñîñòàâíûå ÷àñòè

    319

    Symfony Documentation, Âûïóñê 2.0

    Ïðåêðàùåíèå îáðàáîòêè ñîáûòèé

     íåêîòîðûõ ñëó÷àÿõ, îäèí èç ñëóøàòåëåé ìîæåò çàòðåáîâàòü ïðåêðàùåíèå
    îáðàáîòêè ñîáûòèÿ äðóãèìè ñëóøàòåëÿìè. Äðóãèìè ñëîâàìè, ñëóøàòåëü äîëæåí èìåòü âîçìîæíîñòü ñîîáùèòü äèñïåò÷åðó, ÷òî îí äîëæåí îñòàíîâèòü îáðàáîòêó ñîáûòèÿ âñåìè îñòàâøèìèñÿ ñëóøàòåëÿìè (íå óâåäîìëÿòü èõ î ñîáûòèè). Ýòîãî ìîæíî äîñòèãíóòü âíóòðè ñëóøàòåëÿ ïðè ïîìîùè ìåòîäà
    :method:`Symfony\\Component\\EventDispatcher\\Event::stopPropagation`:
    use Acme\StoreBundle\Event\FilterOrderEvent;
    public function onStoreOrder(FilterOrderEvent $event)
    {
    // ...
    }

    $event->stopPropagation();

    Òåïåðü, âñå ñëóøàòåëè store.order, êîòîðûå åù¼ íå áûëè óâåäîìëåíû î ñîáûòèè, óâåäîìëÿòüñÿ óæå íå áóäóò.
    2.16.4 Ïðîôàéëåð

    Ïðîôàéëåð Symfony2, åñëè îí àêòèâèðîâàí, ñîáèðàåò ïîëåçíóþ èíôîðìàöèþ î êàæäîì
    çàïðîñå, âûïîëíåííîì ê âàøåìó ïðèëîæåíèå è ñîõðàíÿåò åãî äëÿ ïîñëåäóþùåãî àíàëèçà. Èñïîëüçîâàíèå ïðîôàéëåðà â äåâåëîïåðñêîì îêðóæåíèè ïîìîæåò âàì â îòëàäêå êîäà
    è óâåëè÷åíèè áûñòðîäåéñòâèÿ; èñïîëüçóéòå åãî â ïðîäóêòîâîé ñðåäå äëÿ îáíàðóæåíèÿ
    ïðîáëåì ïî ôàêòó.
    Âàì âðÿä ëè ïðèä¼òñÿ ÷àñòî âçàèìîäåéñòâîâàòü ñ ïðîôàéëåðîì íåïîñðåäñòâåííî, òàê
    êàê Symfony2 ïðåäîñòàâëÿåò âèçóàëèçàòîð ïî òèïó Web Debug Toolbar è Web Proler.
    Åñëè âû èñïîëüçóåòå Symfony2 Standard Edition, ïðîôàéëåð, äåáàã-ïàíåëü è âåáïðîôàéëåð óæå íàñòðîåíû è ïîäêëþ÷åíû.

    Ïðèìå÷àíèå: Ïðîôàéëåð ñîáèðàåò èíôîðìàöèþ îáî âñåõ çàïðîñàõ (ïðîñòûå çàïðîñû,

    ïåðåíàïðàâëåíèÿ, èñêëþ÷åíèÿ, Ajax çàïðîñû, ESI çàïðîñû; à òàêæå î âñåõ HTTP ìåòîäàõ è îáî âñåõ ôîðìàòàõ). Ýòî îçíà÷àåò, ÷òî äëÿ îäíîãî URL âû ìîæåòå èìåòü ìíîãî
    ïðîôèëèðîâàííûõ äàííûõ (ïî îäíîìó íà êàæäóþ ïàðó çàïðîñ/îòâåò).

    320

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    Âèçóàëèçàöèÿ äàííûõ ïðîôàéëåðà

    Èñïîëüçîâàíèå Web Debug Toolbar
     dev îêðóæåíèè web debug toolbar ðàñïîëîæåí â íèçó êàæäîé ñòðàíèöû. Îí îòîáðàæàåò îáîáù¼ííûå äàííûå ïðîôàéëåðà è ïðåäîñòàâëÿåò äîñòóï ê ïîëåçíîé èíôîðìàöèè,
    êîãäà ÷òî-ëèáî ðàáîòàåò íå òàê êàê îæèäàëîñü.
    Åñëè îáîáù¼ííûõ äàííûõ íå õâàòàåò, âû ìîæåòå êëèêíóòü íà ññûëêó ñ òîêåíîì ( ñòðîêà
    èç 13 ñëó÷àéíûõ ñèìâîëîâ) è ïåðåéòè íà ñòðàíèöó Web Proler.

    Ïðèìå÷àíèå: Åñëè òîêåí íå êëèêàåòñÿ, ýòî îçíà÷àåò, ÷òî ìàðøðóòû ïðîôàéëåðà íå
    çàðåãèñòðèðîâàíû (ñì. íèæå èíôîðìàöèþ î êîíôèãóðèðîâàíèè).

    Àíàëèç äàííûõ â Web Proler
    Web Proler - ýòî èíñòðóìåíò âèçóàëèçàöèè äàííûõ ïðîôèëèðîâàíèÿ, êîòîðûé âû ìîæåòå èñïîëüçîâàòü â ðàçðàáîòêå äëÿ îòëàäêè âàøåãî êîäà è óâåëè÷åíèÿ åãî áûñòðîäåéñòâèÿ; íî åãî òàêæå ìîæíî èñïîëüçîâàòü äëÿ îòñëåæèâàíèÿ ïðîáëåì â ïðîäóêòîâîé ñðåäå. Îí ïðåäîñòàâëÿåò âñþ èíôîðìàöèþ, ñîáðàííóþ ïðîôàéëåðîì, â ñâî¼ì âåáèíòåðôåéñå.

    Äîñòóï ê äàííûì ïðîôàéëåðà
    Âàì íå îáÿçàòåëüíî èñïîëüçîâàòü âèçóàëèçàòîð äëÿ äîñòóïà ê äàííûì ïðîôàéëåðà. Êàê
    æå âàì ïîëó÷èòü äîñòóï ê èíôîðìàöèè ïðîôàéëåðà äëÿ íåêîòîðîãî çàïðîñà ïî ôàêòó
    åãî âûïîëíåíèÿ? Êîãäà ïðîôàéëåð ñîõðàíÿåò äàííûå î çàïðîñå, îí òàêæå àññîöèèðóåò
    ñ íèìè íåêîòîðûé òîêåí; ýòîò òîêåí äîñòóïåí â çàãîëîâêå îòâåòà X-Debug-Token:
    $profile = $container->get('profiler')->loadProfileFromResponse($response);
    $profile = $container->get('profiler')->loadProfile($token);

    Ñîâåò: Êîãäà ïðîôàéëåð àêòèâèðîâàí, íî íåò web debug toolbar, èëè æå êîãäà âû
    õîòèòå ïîëó÷èòü òîêåí äëÿ Ajax çàïðîñà, èñïîëüçóéòå, íàïðèìåð, Firebug äëÿ òîãî,
    ÷òîáû ïîëó÷èòü çàãîëîâîê X-Debug-Token.

    Èñïîëüçóéòå ìåòîä find(), äëÿ ïîëó÷åíèÿ äîñòóïà ê òîêåíàì ïî êàêîìó-ëèáî êðèòåðèþ:
    // ïîëó÷èòü 10 ïîñëåäíèõ òîêåíîâ
    $tokens = $container->get('profiler')->find('', '', 10);

    2.16.

    Ñîñòàâíûå ÷àñòè

    321

    Symfony Documentation, Âûïóñê 2.0

    // ïîëó÷èòü ïîñëåäíèå 10 òîêåíîâ äëÿ âñåõ URL, ñîäåðæàùèõ /admin/
    $tokens = $container->get('profiler')->find('', '/admin/', 10);
    // ïîëó÷èòü ïîñëåäíèå 10 òîêåíîâ äëÿ ëîêàëüíûõ çàïðîñîâ
    $tokens = $container->get('profiler')->find('127.0.0.1', '', 10);

    Åñëè âû õîòèòå ìàíèïóëèðîâàòü äàííûìè ïðîôàéëåðà íà äðóãîé ìàøèíå, èñïîëüçóéòå
    ìåòîäû export() è import():
    // â prod îêðóæåíèè
    $profile = $container->get('profiler')->loadProfile($token);
    $data = $profiler->export($profile);
    // â dev îêðóæåíèè
    $profiler->import($data);

    Êîíôèãóðèðîâàíèå
    Êîíôèãóðàöèÿ ïî óìîë÷àíèþ ñîäåðæèò ðàçóìíûå íàñòðîéêè ïðîôàéëåðà, äåáàã-ïàíåëè
    (web debug toolbar) è âåá-ïðîôàéëåðà (web proler). Íèæå ïðèâåä¼í ïðèìåð êîíôèãóðàöèè äëÿ dev îêðóæåíèÿ:
    ˆ

    YAML

    # çàãðóçêà ïðîôàéëåðà
    framework:
    profiler: { only_exceptions: false }
    # àêòèâàöèÿ âåá-ïðîôàéëåðà
    web_profiler:
    toolbar: true
    intercept_redirects: true
    verbose: true

    ˆ

    XML







    toolbar="true"
    322

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    />

    ˆ

    intercept-redirects="true"
    verbose="true"

    PHP

    // çàãðóçêà ïðîôàéëåðà
    $container->loadFromExtension('framework', array(
    'profiler' => array('only-exceptions' => false),
    ));
    // àêòèâàöèÿ âåá-ïðîôàéëåðà
    $container->loadFromExtension('web_profiler', array(
    'toolbar' => true,
    'intercept-redirects' => true,
    'verbose' => true,
    ));

    Åñëè only-exceptions èìååò çíà÷åíèå true, ïðîôàéëåð ñîáèðàåò äàííûå òîëüêî ïðè
    âîçíèêíîâåíèè èñêëþ÷åíèé.
    Åñëè intercept-redirects èìååò çíà÷åíèå true, ïðîôàéëåð ïåðåõâàòûâàåò ïåðåíàïðàâëåíèÿ è ïðåäîñòàâëÿåò âàì âîçìîæíîñòü íàáëþäàòü ñîáðàííûå äàííûå ïåðåä ïåðåíàïðàâëåíèåì.
    Åñëè verbose èìååò çíà÷åíèå true, Web Debug Toolbar îòîáðàæàåò áîëüøîå êîëè÷åñòâî äàííûõ. Åñëè ïðèñâîèòü verbose çíà÷åíèå false, âòîðè÷íàÿ èíôîðìàöèÿ íå áóäåò
    îòîáðàæàòüñÿ.
    Åñëè âû àêòèâèðîâàëè web proler, âàì òàêæå íåîáõîäèìî ïîäêëþ÷èòü åãî ìàðøðóòû:
    ˆ

    YAML

    _profiler:
    resource: @WebProfilerBundle/Resources/config/routing/profiler.xml
    prefix: /_profiler

    ˆ

    XML


    Òàê êàê ïðîôàéëåð âûïîëíÿåò äîïîëíèòåëüíóþ ðàáîòó äëÿ êàæäîãî çàïðîñà, âû, âîçìîæíî, çàõîòèòå àêòèâèðîâàòü åãî â ïðîäóêòîâîé ñðåäå ëèøü â íåêîòîðûõ ñëó÷àÿõ.
    Îïöèÿ only-exceptions óñòàíàâëèâàåò ëèìèò ïðîôèëèðîâàíèÿ â 500 ñòðàíèö, íî ÷òî,

    2.16.

    Ñîñòàâíûå ÷àñòè

    323

    Symfony Documentation, Âûïóñê 2.0

    åñëè âû çàõîòèòå ïîëó÷èòü èíôîðìàöèþ, êîãäà IP êëèåíòà èìååò íåêîòîðîå îïðåäåë¼ííîå çíà÷åíèå èëè åñëè çàïðàøèâàåòñÿ ñòðîãî îïðåäåë¼ííàÿ ÷àñòü ñàéòà? Âû ìîæåòå
    èñïîëüçîâàòü request matcher:
    ˆ

    YAML

    # àêòèâèðóåò ïðîôàéëåð äëÿ çàïðîñîâ èç ïîäñåòè 192.168.0.0/24
    framework:
    profiler:
    matcher: { ip: 192.168.0.0/24 }
    # àêòèâèðóåò ïðîôàéëåð òîëüêî äëÿ URL /admin
    framework:
    profiler:
    matcher: { path: "^/admin/" }
    # êîìáèíèðîâàíèå ïðàâèë
    framework:
    profiler:
    matcher: { ip: 192.168.0.0/24, path: "^/admin/" }
    # èñïîëüçîâàíèå ïîëüçîâàòåëüñêîé ñëóæáû matcher
    framework:
    profiler:
    matcher: { service: custom_matcher }

    ˆ

    XML




















    324

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0








    ˆ

    PHP

    // àêòèâèðóåò ïðîôàéëåð äëÿ çàïðîñîâ èç ïîäñåòè 192.168.0.0/24
    $container->loadFromExtension('framework', array(
    'profiler' => array(
    'matcher' => array('ip' => '192.168.0.0/24'),
    ),
    ));
    // àêòèâèðóåò ïðîôàéëåð òîëüêî äëÿ URL /admin
    $container->loadFromExtension('framework', array(
    'profiler' => array(
    'matcher' => array('path' => '^/admin/'),
    ),
    ));
    // êîìáèíèðîâàíèå ïðàâèë
    $container->loadFromExtension('framework', array(
    'profiler' => array(
    'matcher' => array('ip' => '192.168.0.0/24', 'path' => '^/admin/'),
    ),
    ));
    # èñïîëüçîâàíèå ïîëüçîâàòåëüñêîé ñëóæáû matcher
    $container->loadFromExtension('framework', array(
    'profiler' => array(
    'matcher' => array('service' => 'custom_matcher'),
    ),
    ));

    2.16.5 ×èòàéòå â êíèãå ðåöåïòîâ

    ˆ

    Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå

    ˆ /cookbook/profiler/data_collector
    ˆ /cookbook/event_dispatcher/class_extension
    ˆ /cookbook/event_dispatcher/method_behavior
    2.16.

    Ñîñòàâíûå ÷àñòè

    325

    Symfony Documentation, Âûïóñê 2.0

    2.17 Ñòàáèëüíûé API Symfony2
    Ñòàáèëüíûé API Symfony2 ýòî ïîäìíîæåñòâî âñåõ îïóáëèêîâàííûõ ìåòîäîâ Symfony2
    (êàê êîìïîíåíòîâ, òàê è ïàêåòîâ èç ñîñòàâà ÿäðà), êîòîðûå îáúåäèíåíû ïî ñëåäóþùèì
    ïðèçíàêàì:
    ˆ Ïðîñòðàíñòâî èì¼í è èìÿ êëàññà íå áóäåò èçìåíÿòüñÿ;
    ˆ Íàèìåíîâàíèå ìåòîäà íå áóäåò èçìåíÿòüñÿ;
    ˆ Ñèãíàòóðà ìåòîäà (àðãóìåíòû è òèï âîçâðàùàåìîãî çíà÷åíèÿ) íå áóäåò èçìåíÿòüñÿ;
    ˆ Ñåìàíòèêà òîãî, ÷òî ìåòîä äåëàåò íå áóäåò èçìåíÿòüñÿ;
    Õîòÿ, ðåàëèçàöèÿ ìåòîäà ìîæåò ìåíÿòüñÿ ñî âðåìåíåì. Åäèíñòâåííûé ñëó÷àé, îïðàâäûâàþùèé èçìåíåíèå ñòàáèëüíîãî API - èñïðàâëåíèå äûð â áåçîïàñíîñòè.
    Ñòàáèëüíûé API îñíîâûâàåòñÿ íà ñïèñêå whitelist, òàãèðîâàííîì @api. Ïî ýòîé ïðè÷èíå
    âñ¼, ÷òî íå èìååò ýòîãî òàãà - íå ÿâëÿåòñÿ ÷àñòüþ ñòàáèëüíîãî API.

    Ñîâåò: Ëþáîé ñòîðîííèé ïàêåò ìîæåò òàêæå ïóáëèêîâàòü ñâîé ñîáñòâåííûé ñòàáèëüíûé API.

    Íà÷èíàÿ ñ Symfony 2.0, ñëåäóþùèå êîìïîíåíòû èìåþò ïóáëè÷íûé API:
    ˆ BrowserKit
    ˆ ClassLoader
    ˆ Console
    ˆ CssSelector
    ˆ DependencyInjection
    ˆ DomCrawler
    ˆ EventDispatcher
    ˆ Finder
    ˆ HttpFoundation
    ˆ HttpKernel
    ˆ Locale
    ˆ Process
    ˆ Routing
    ˆ Templating
    326

    Ãëàâà 2.

    Êíèãà

    Symfony Documentation, Âûïóñê 2.0

    ˆ Translation
    ˆ Validator
    ˆ Yaml
    ˆ

    Symfony2 è îñíîâû HTTP

    ˆ

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    ˆ

    Óñòàíîâêà è íàñòðîéêà Symfony2

    ˆ

    Ñîçäàíèå ñòðàíèö â Symfony2

    ˆ

    Êîíòðîëëåð

    ˆ

    Ìàðøðóòèçàöèÿ

    ˆ

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    ˆ

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    ˆ

    Òåñòèðîâàíèå

    ˆ

    Âàëèäàöèÿ

    ˆ

    Ôîðìû

    ˆ

    Áåçîïàñíîñòü

    ˆ

    HTTP Êýøèðîâàíèå

    ˆ

    Ïåðåâîäû

    ˆ

    Êîíòåéíåð ñëóæá

    ˆ /book/performance
    ˆ

    Ñîñòàâíûå ÷àñòè

    ˆ

    Ñòàáèëüíûé API Symfony2

    ˆ

    Symfony2 è îñíîâû HTTP

    ˆ

    Symfony2 ïðîòèâ ÷èñòîãî PHP

    ˆ

    Óñòàíîâêà è íàñòðîéêà Symfony2

    ˆ

    Ñîçäàíèå ñòðàíèö â Symfony2

    ˆ

    Êîíòðîëëåð

    ˆ

    Ìàðøðóòèçàöèÿ

    ˆ

    Ñîçäàíèå è èñïîëüçîâàíèå Øàáëîíîâ

    ˆ

    Áàçû äàííûõ è Doctrine (Ìîäåëü)

    ˆ

    Òåñòèðîâàíèå

    2.17.

    Ñòàáèëüíûé API Symfony2

    327

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    Âàëèäàöèÿ

    ˆ

    Ôîðìû

    ˆ

    Áåçîïàñíîñòü

    ˆ

    HTTP Êýøèðîâàíèå

    ˆ

    Ïåðåâîäû

    ˆ

    Êîíòåéíåð ñëóæá

    ˆ /book/performance
    ˆ

    Ñîñòàâíûå ÷àñòè

    ˆ

    Ñòàáèëüíûé API Symfony2

    328

    Ãëàâà 2.

    Êíèãà

    ×àñòü III
    Ðåöåïòû

    329

    Ãëàâà

    3

    Cookbook
    3.1 Êàê ñîçäàòü è ðàçìåñòèòü Ïðîåêò íà Symfony2 â
    git-ðåïîçèòîðèè

    Ñîâåò: Íåñìîòðÿ íà òî, ÷òî ýòà ñòàòüÿ ïîñâåùÿíà git, îñíîâíûå ïðèíöèïû, îïèñàííûå
    òóò, àêòóàëüíû è äëÿ Subversion.

    Åñëè Âû óæå ïðî÷èòàëè ñòàòüþ Ñîçäàíèå ñòðàíèö â Symfony2 è íåìíîãî ïîçíàêîìèëèñü ñ Symfony, ñìåëî ìîæíî ñîçäàâàòü ñâîé ñîáñòâåííûé ïðîåêò. Ýòîò ðåöåïò ïîçíàêîìèò Âàñ ñ ëó÷øèì ñïîñîáîì ñîçäàíèÿ ïðîåêòà íà Symfony2 ñ èñïîëüçîâàíèåì ñèñòåìû
    êîíòðîëÿ âåðñèé git.
    3.1.1 Ïðåäâàðèòåëüíàÿ íàñòðîéêà ïðîåêòà

    Äëÿ íà÷àëà Âàì íóæíî ñêà÷àòü Symfony è èíèöèàëèçèðîâàòü Âàø ëîêàëüíûé git ðåïîçèòîðèé:
    1. Ñêà÷àéòå Symfony2 Standard Edition áåç ñòîðîííûõ áèáëèîòåê (without vendors).
    2. Ðàñïàêóéòå äåñòðèáóòèâ. Áóäåò ñîçäàíà äèðåêòîðèÿ Symfony ñ áàçîâîé ñòðóêòóðîé
    ïðîåêòà, ôàéëàìè êîíôèãóðàöèè è ò.ä. Ïåðåèìåíóéòå åå, êàê ñî÷òåòå íóæíûì.
    3. Ñîçäàéòå íîâûé ôàéë ñ èìåíåì .gitignore â êîðíå Âàøåãî ïðîåêòà è âñòàâüòå â
    íåãî ñëåäóþùèé êîä. Âñå ôàéëû, ñîâïàäàþùèå ñ ïåðå÷èñëåííûìè øàáëîíàìè, git
    áóäåò èãíîðèðîâàòü:
    /web/bundles/
    /app/bootstrap*
    /app/cache/*
    331

    Symfony Documentation, Âûïóñê 2.0

    /app/logs/*
    /vendor/
    /app/config/parameters.yml

    4. Ñêîïèðóéòå app/config/parameters.yml â app/config/parameters.yml.dist.
    Ôàéë parameters.yml èãíîðèðóåòñÿ git'îì (ñìîòðèòå âûøå), ïîýòîìó òàêèå
    ìàøèíî-çàâèñèìûå íàñòðîéêè, êàê íàïðèìåð ïàðîëü îò áàçû äàííûõ, íå áóäóò îòïðàâëÿòüñÿ â ðåïîçèòîðèé. Èìåÿ ôàéë app/config/parameters.yml.dist â ðåïîçèòîðèè, íîâûå ðàçðàáîò÷èêè ñìîãóò áûñòðî âûãðóçóòü ïðîåêò, ñêîïèðîâàòü ýòîò
    ôàéë â parameters.yml, íàñòðîèòü åãî è íà÷àòü ðàçðàáîòêó.
    5. Èíèöèàëèçèðóéòå Âàø git ðåïîçèòîðèé:
    $ git init

    6. Äîáàâüòå âñå íà÷àëüíûå ôàéëû â git:
    $ git add .

    7. Ñîçäàéòå ïåðâûé êîììèò Âàøåãî ïðîåêòà:
    $ git commit -m "Initial commit"

    8. Íàêîíåö, ñêà÷àéòå âñå ñòîðîííèå áèáëèîòåêè:
    $ php bin/vendors install

    Íà ýòîì ìîìåíòå Âû èìååòå ïîëíîñòüþ ôóíêöèîíèðóþùèé ïðîåêò íà Symfony2, êîòîðûé ïðàâèëüíî ðàçìåùåí â git. Âû ìîæåòå íà÷èíàòü ïðîãðàììèðîâàòü è îòïðàâëÿòü
    èçìåíåíèÿ â Âàø ðåïîçèòîðèé.
    Ñåé÷àñ Âû ìîæåòå ïåðåêëþ÷èòüñÿ íà ñòàòüþ Ñîçäàíèå ñòðàíèö
    íèÿ âîïðîñîâ êîíôèãóðàöèè è ðàçðàáîòêè Âàøåãî ïðèëîæåíèÿ.

    â Symfony2

    äëÿ èçó÷å-

    Ñîâåò: Ñòàíäàðòíàÿ âåðñèÿ Symfony2 ñîäåðæèò â ñåáå íåêîòîðûé ôóíêöèîíàë äëÿ

    äåìîíñòðàöèè. ×òîáû óáðàòü äåìîíñòðàöèîííûé êîä, ñëåäóéòå èíñòðóêöèÿì Standard
    Edition Readme.

    3.1.2 Óïðàâëåíèå

    âíåøíèìè

    áèáëèîòåêàìè

    ñ

    ïîìîùüþ

    bin/vendors è deps

    Êàæäûé ïðîåêò íà Symfony èñïîëüçóåò áîëüøóþ ãðóïïó ñòîðîííèõ áèáëèîòåê.
    Ïî óìîë÷àíèþ, ýòè áèáëèîòåêè ñêà÷èâàþòñÿ ïðè çàïóñêå êîìàíäû php bin/vendors
    install. Ýòîò ñêðèïò ÷èòàåò èç ôàéëà deps è ñêà÷èâàåò ïîëó÷åííûå áèáëèîòåêè â

    332

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    ïàïêó vendor/. Îí òàêæå ÷èòàåò ôàéë deps.lock è ñâÿçûâàåò êàæäóþ óïîìÿíóòóþ
    áèáëèîòåêó ñ óêàçàííûì git õåøåì â ðåïîçèòîðèè.
     íàøåì ïðîåêòå ñòîðîííèå áèáëèîòåêè íå ÿâëÿþòñÿ ÷àñòüþ git ðåïîçèòîðèÿ. Îíè òàêæå
    íå ÿâëÿþòñÿ äî÷åðíèìè ìîäóëÿìè (submodules). Âìåñòî ýòîãî ìû ïîëàãàåìñÿ íà ôàéëû deps è deps.lock, à òàêæå íà ñêðèïò bin/vendors, êîòîðûé âñåì óïðàâëÿåò. Ýòè
    ôàéëû ëåæàò â ðåïîçèòîðèè, è êàæäàÿ âåðñèÿ ïðîåêòà èñïîëüçóåò íåîáõîäèìûå âåðñèè
    ñòîðîííèõ áèáëèîòåê. Ïîëó÷àåòñÿ, Âû ìîæåòå èñïîëüçîâàòü ñêðèïò vendors, ÷òîáû Âàø
    ïðîåêò áûë â àêòóàëüíîì ñîñòîÿíèè.
    Âñÿêèé ðàç, êîãäà ðàçðàáîò÷èê êîïèðóåò ïðîåêò, îí äîëæåí âûïîëíèòü ñêðèïò php
    bin/vendors install ÷òîáû óáåäèòüñÿ, ÷òî âñå íåîáõîäèìûå ñòîðîííèå áèáëèîòåêè
    óñòàíîâëåíû.

    Îáíîâëåíèå Symfony
    Ïîñêîëüêó Symfony ýòî ãðóïïà ñòîðîííèõ áèáëèîòåê è ýòè ñòîðîííèå áèáëèîòåêè
    ïîëíîñòüþ êîíòðîëèðóþòñÿ ÷åðåç deps è deps.lock, îáíîâëåíèå Symfony - ýòî ïðîñòîå îáíîâëåíèå ýòèõ ôàéëîâ äî ñîñòîÿíèÿ èõ â ïîñëåäíåé âåðñèè Symfony Standard
    Edition.
    Êîíå÷íî æå, åñëè Âû äîáàâèëè äîïîëíèòåëüíûå èñòî÷íèêè â deps èëè deps.lock,
    óáåäèòåñü, ÷òî îáíîâëÿåòå òîëüêî îðèãèíàëüíûå èñòî÷íèêè (ò.å. íå òðîãàéòå Âàøè
    äîïîëíèòåëüíûå èñòî÷íèêè).

    Îñòîðîæíî: Òàêæå ñóùåñòâóåò êîìàíäà php bin/vendors update, íî îíà íå ïðåäíàçíà÷åíà äëÿ îáíîâëåíèÿ Âàøåãî ïðîåêòà è Âàì íå íàäî åå èñïîëüçîâàòü â ðàáîòå.
    Ýòà êîìàíäà ñëóæèò äëÿ ôèêñàöèè âåðñèé âñåõ Âàøèõ ñòîðîííèõ áèáëèîòåê ïðè îáíîâëåíèé èõ äî âåðñèè, óêàçàííîé â deps, è çàïèñûâàåò èõ õåøè â ôàéë deps.lock.
    Êðîìå òîãî, åñëè Âàì çàõîòåëîñü îáíîâèòü ôàéë deps.lock â ñîîòâåòñòâèè ñ óñòàíîâëåííûìè ñòîðîííèìè áèáëèîòåêàìè, òî ïðîñòî çàïóñòèòå êîìàíäó php bin/vendors
    lock ÷òîáû ñîõðàíèòü ñîîòâåòñòâóþùèå git SHA èäåíòèôèêàòîðû â ôàéëå deps.lock.

    Ñòîðîííèå áèáëèîòåêè è Äî÷åðíèå ìîäóëè

    Âìåñòî òîãî, ÷òîáû èñïîëüçîâàòü deps è ñêðèïò bin/vendors äëÿ óïðàâëåíèÿ Âàøèìè
    ñòîðîííèìè áèáëèîòåêàìè, Âû ìîæåòå èñïîëüçîâàòü ðîäíûå git submodules. Íåò íè÷åãî
    ïëîõîãî â ýòîì âûáîðå, íî ñèñòåìà deps - ýòî îôèöèàëüíîå ðåøåíèå ýòîé ïðîáëåìû è
    äî÷åðíèå ìîäóëè git'à ìîãóò âíåñòè äîïîëíèòåëüíûå ñëîæíîñòè â ðàáîòó.
    3.1.3 Õðàíåíèå Âàøåãî ïðîåêòà íà Óäàëåííîì Ñåðâåðå

    Ñåé÷àñ ó Âàñ èìååòñÿ ïîëíîñòüþ ôóíêöèîíèðóþùèé ïðîåêò íà Symfony2, ñîõðàíåííûé
    â git. Òåì íå ìåíåå, âî ìíîãèõ ñëó÷àÿõ Âàì ïîíàäîáèòñÿ õðàíèòü ïðîåêò íà óäàëåííîì
    3.1.

    Êàê ñîçäàòü è ðàçìåñòèòü Ïðîåêò íà Symfony2 â git-ðåïîçèòîðèè

    333

    Symfony Documentation, Âûïóñê 2.0

    ñåðâåðå. Íàïðèìåð äëÿ õðàíåíèÿ ðåçåðâíîé êîïèè ïðîåêòà èëè ÷òîáû äðóãèå ðàçðàáîò÷èêè òàêæå èìåëè äîñòóï ê ïðîåêòó äëÿ ñîâìåñòíîé ðàáîòû.
    Ñàìûé ïðîñòîé ñïîñîá õðàíèòü Âàø ïðîåêò íà óäàëåííîì ñåðâåðå - ýòî GitHub. Íà
    íåì ïóáëè÷íûé ðåïîçèòîðèè áåñïëàòíû. Çà çàêðûòûå ðåïîçèòîðèè Âàì íóæíî áóäåò
    ïëàòèòü åæåìåñÿ÷íóþ ïëàòó.
    Ñ äðóãîé ñòîðîíû, Âû ìîæåòå õðàíèòü Âàø git ðåïîçèòîðèé íà ëþáîì ñåðâåðå. Äîñòàòî÷íî ëèøü ñîçäàòü barebones repository è çàãðóçèòü äàííûå íà íåãî. Áèáëèîòåêà
    Gitolite ìîæåò ïîìî÷ü Âàì â ýòîì ïðîöåññå.

    3.2 Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê
    Êîãäà ïðîèñõîäèò êàêîå-ëèáî èñêëþ÷åíèå â Symfony2, îíî ïåðåõâàòûâàåòñÿ âíóòðè êëàññà Kernel è â êîíå÷íîì ñ÷åòå ïåðåíàïðàâëÿåòñÿ ñïåöèàëüíîìó êîíòðîëëåðó,
    TwigBundle:Exception:show äëÿ îáðàáîòêè. Äàííûé êîíòðîëëåð, êîòîðûé ðàñïîëîæåí
    â ÿäðå ïàêåòà TwigBundle, îïðåäåëÿåò êàêîé èç øàáëîíîâ îøèáîê ïîêàçàòü, è êàêîé
    óñòàíîâèòü êîä îøèáêè äàííîìó èñêëþ÷åíèþ.

    Ñîâåò: Ñïîñîáîâ íàñòðîéêè ïåðåõâàòà èñêëþ÷åíèé ãîðàçäî áîëüøå, ÷åì îïèñàíî çäåñü.

    Îáðàáîòêà âíóòðåííåãî ñîáûòèÿ kernel.exception, êîòîðîå âîçíèêàåò ïðè âîçíèêíîâåíèè èñêëþ÷åíèé ïîçâîëÿåò ïîëíîñòüþ ïîëó÷èòü êîíòðîëü íàä îáðàáîòêîé èñêëþ÷åíèé.
    Äëÿ ïîëó÷åíèÿ äîïîëíèòåëüíîé èíôîðìàöèè ñì. Ñîáûòèå kernel.exception .
    Âñå øàáëîíû îøèáîê ðàçìåùåíû âíóòðè ïàêåòà TwigBundle. Äëÿ ïåðåîïðåäåëåíèÿ øàáëîíîâ, ñëåäóåò èñïîëüçîâàòü ñòàíäàðòíûé ñïîñîá ïåðåîïðåäåëåíèÿ øàáëîíîâ, êîòîðûå
    ðàçìåùåíû âíóòðè ïàêåòà. Äëÿ ïîëó÷åíèÿ äîïîëíèòåëüíîé èíôîðìàöèè ñì. overidingbundle-templates.
    Íàïðèìåð, ÷òîáû
    âàåòñÿ êîíå÷íîìó

    ïåðåîïðåäåëèòü
    ïîëüçîâàòåëþ,

    øàáëîí ïî-óìîë÷àíèþ, êîòîðûé ïîêàçûñîçäàéòå øàáëîí ðàñïîëîæåííûé çäåñü:
    app/Resources/TwigBundle/views/Exception/error.html.twig:




    An Error Occurred: {{ status_text }}


    Oops! An Error Occurred


    The server returned a "{{ status_code }} {{ status_text }} ".





    334

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Åñëè âû íå çíàêîìû ñ Twig'îì, íå ñòîèò ïåðåæèâàòü. Twig - ïðîñòîé, ìîùíûé
    è íåîáÿçàòåëüíûé øàáëîíèçàòîð, êîòîðûé èíòåãðèðîâàí ñ Symfony2.

     äîáàâîê ê îáû÷íîé HTML ñòðàíèöå îøèáîê, Symfony ïðåäîñòàâëÿåò äîñòóï ê
    ñòðàíèöàì îøèáîê äëÿ ñàìûõ ðàñïðîñòðåííûõ ôîðìàòîâ îòâåòîâ, âêëþ÷àÿ JSON
    (error.json.twig), XML, (error.xml.twig), è äàæå Javascript (error.js.twig).
    È ìû íàçâàëè âñåãî íåñîêîëüêî èç íèõ. Äëÿ ïåðåîïðåäåëåíèÿ ëþáîãî èç
    ýòèõ øàáëîíîâ, ïðîñòî ñîçäàéòå íîâûé ôàéë ñ òåì æå èìåíåì â êàòàëîãå
    app/Resources/TwigBundle/views/Exception. Òàêîé ñïîñîá ÿâëÿåòñÿ ñòàíäàðòíûì, ñ
    ïîìîùüþ êîòîðîãî ïåðåîïðåäåëÿþòñÿ ëþáûå øàáëîíû êîòîðûå åñòü â ïàêåòå.
    3.2.1 Íàñòðîéêà 404 ñòðàíèöû è äðóãèõ ñòðàíèö îøèáîê

    Òàêæå
    âû
    ìîæåòå
    âèñèìîñòè
    îò
    êîäà

    íàñòðîèòü
    ñîñòîÿíèÿ

    îòäåëüíûå
    øàáëîíû
    HTTP.
    Íàïðèìåð,

    îøèáîê
    â
    çàñîçäàéòå
    øàáëîí
    app/Resources/TwigBundle/views/Exception/error404.html.twig äëÿ îòîáðàæåíèÿ ñïåöèàëüíîé ñòðàíèöû äëÿ 404 îøèáêè (ñòðàíèöà íå íàéäåíà).
    Äëÿ îïðåäåëåíèÿ, êàêîé øàáëîí èñïîëüçîâàòü, Symfony èñïîëüçóåò ñëåäóþùèé àëãîðèòì:
    ˆ Ñïåðâà, îí èùåò øàáëîí äëÿ òåêóùåãî ôîðìàòà è êîäà ñîñòîÿíèÿ (íàïðèìåð
    error404.json.twig);
    ˆ Åñëè òàêîãî øàáëîíà íå ñóùåñòâóåò, òîãäà îí èùåò øàáëîí äëÿ òåêóùåãî ôîðìàòà
    (íàïðèìåð error.json.twig);
    ˆ Åñëè òàêîãî øàáëîíà íå ñóùåñòâóåò, òîãäà îí âîçâðàùàåòñÿ ê HTML øàáëîíó
    (íàïðèìåð error.html.twig).

    Ñîâåò:

    Ïîëíûé
    ñïèñîê
    ñòàíäàðòíûõ
    øàáëîíîâ
    îøèáîê
    íàõîäèòñÿ â êàòàëîãå Resources/views/Exception, ïàêåòà TwigBundle. Â ñòàíäàðòíîé
    ïîñòàâêå
    Symfony2,
    ïàêåò
    TwigBundle íàõîäèòñÿ â êàòàëîãå
    vendor/symfony/src/Symfony/Bundle/TwigBundle. ×àùå âñåãî, ñàìûì ïðîñòûì ñïîñîáîì íàñòðîéêè ñòðàíèöû îøèáîê, ÿâëÿåòñÿ å¼ êîïèðîâàíèå èç ïàêåòà TwigBundle â
    êàòàëîã app/Resources/TwigBundle/views/Exception è ïîñëåäóþùåå ðåäàêòèðîâàíèå.

    Ïðèìå÷àíèå: Ñòðàíèöû-èñêëþ÷åíèÿ, êîòîðûå óäîáíû äëÿ îòëàäêè è êîòîðûå äåìîí-

    ñòðèðóþòñÿ ðàçðàáîò÷èêó òàêæå ìîãóò áûòü íàñòðîåíû äàííûì ñïîñîáîì - ñîçäàéòå
    øàáëîíû exception.html.twig äëÿ ñòàíäàðòíîé HTML ñòðàíèöû îøèáîê èëè ñîîòâåñòâåííî exception.json.twig äëÿ JSON.

    3.2.

    Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê

    335

    Symfony Documentation, Âûïóñê 2.0

    3.3 Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ
    Èç ðóêîâîäñòâà, âû óçíàëè, ÷òî ðàáîòàòü ñ êîíòðîëëåðîì ëåã÷å, åñëè îí ðàñøèðÿåò áàçîâûé êëàññ Symfony\Bundle\FrameworkBundle\Controller\Controller. Äàííûé ñïîñîá
    õîðîøî ðàáîòàåò, îäíàêî êîíòðîëëåð ìîæíî îïðåäåëèòü â âèäå ñëóæáû.
    ×òîáû ñîñëàòüñÿ íà êîíòðîëëåð êîòîðûé îïðåäåëåí â âèäå ñëóæáû, ñëåäóåò èñïîëüçîâàòü íîòàöèþ (îáîçíà÷åíèå) ñ îäíèì çíàêîì äâîåòî÷èÿ (:). Íàïðèìåð, ìû îïðåäåëèëè
    ñåðâèñ ñ èìåíåì my_controller è õîòèì âûçâàòü ìåòîä indexAction() âíóòðè íåãî:
    $this->forward('my_controller:indexAction', array('foo' => $bar));

    Òàêæå íåîáõîäèìî èñïîëüçîâàòü òàêóþ æå çàïèñü äëÿ çíà÷åíèé ìàðøðóòà _controller
    my_controller:
    pattern: /
    defaults: { _controller: my_controller:indexAction }

    Ïðè òàêîì ñïîñîáå èñïîëüçîâàíèÿ êîíòðîëëåðà, îí äîëæåí áûòü îïðåäåëåí â íàñòðîéêàõ
    êîíòåéíåðà ñåðâèñîâ. Äëÿ ïîëó÷åíèÿ äîïîëíèòåëüíîé èíôîðìàöèè ñì. ãëàâó Service
    Container

    Êîíòðîëëåðû îïðåäåëåííûå êàê ñåðâèñû, ñêîðåå âñåãî íå áóäóò íàñëåäíèêàìè áàçîâîãî
    êëàññà Controller. Âìåñòî òîãî, ÷òîáû èñïîëüçîâàòü ìåòîäû êîòîðûå îí ïðåäîñòàâëÿåò,
    ñêîðåå âñåãî âû áóäåòå ðàáîòàòü íåïîñðåäñòâåííî ñî ñëóæáàìè êîòîðûå íåîáõîäèìû
    èìåííî âàì. Ê ñ÷àñòüþ, ðåøåíèÿ ìíîãèõ ðàñïðîñòðàíåííûõ çàäà÷ íå ñîïðîâîæäàåòñÿ
    áîëüøèìè ñëîæíîñòÿìè è áàçîâûé êëàññ Controller ÿâëÿåòñÿ õîðîøèì èñòî÷íèêîì
    çíàíèé,

    Ïðèìå÷àíèå: Îïðåäåëåíèå êîíòðîëëåðà â âèäå ñëóæáû òðåáóåò íåìíîãî áîëüøå óñè-

    ëèé, ÷åì ïðîñòî êîíòðîëëåðà. Îñíîâíûì ïðåèìóùåñòâîì ñåðâèñà ÿâëÿåòñÿ òî, ÷òî âåñü
    êîíòðîëëåð èëè ëþáàÿ ñëóæáà êîòîðàÿ ïåðåäàåòñÿ êîíòðîëëåðó, ìîæåò áûòü èçìåíåíà
    ÷åðåç íàñòðîéêó êîíòåéíåðà ñåðâèñîâ. Ïðè ðàçðàáîòêå ïàêåòîâ ñ îòêðûòûì èñõîäíûì
    êîäîì èëè ïàêåòà, êîòîðûé áóäåò èñïîëüçîâàí âî ìíîæåñòâå ðàçíûõ ïðîåêòîâ, äàííîå
    ïðåèìóùåñòâî ïðåäñòàâëÿåòñÿ îñîáåííî ïðèìåíèìûì. Äàæå åñëè âû íå áóäåòå èñïîëüçîâàòü êîíòðîëëåðû â êà÷åñòâå ñëóæá, èõ ïðèìåíåíèå ìîæíî áóäåò îáíàðóæèòü â ïàêåòàõ
    Symfony2 ñ îòêðûòûì èñõîäíûì êîäîì.

    336

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    3.4 Êàê çàñòàâèòü ìàðøðóòèçàòîð âñåãäà èñïîëüçîâàòü HTTPS èëè HTTP
    Èíîãäà Âàì íåîáõîäèìî óñòàíîâèòü çàùèùåííîå ñîåäèíåíèå äëÿ ðåñóðñà è Âû õîòèòå
    áûòü óâåðåííûìè, ÷òî äîñòóï ê ýòîìó ðåñóðñó áóäåò âñåãäà îñóùåñòâëÿòüñÿ ÷åðåç ïðîòîêîë HTTPS. Êîìïîíåíò ìàðøðóòèçàöèè ïîçâîëÿåò Âàì íàñòðîèòü ïðèíóäèòåëüíîå
    èñïîëüçîâàíèå ñõåìû URI ñ ïîìîùüþ ïàðàìåòðà _scheme:
    ˆ

    YAML

    secure:
    pattern: /secure
    defaults: { _controller: AcmeDemoBundle:Main:secure }
    requirements:
    _scheme: https

    ˆ

    XML



    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing

    AcmeDemoBundle:Main:secure
    https



    ˆ

    PHP

    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    $collection = new RouteCollection();
    $collection->add('secure', new Route('/secure', array(
    '_controller' => 'AcmeDemoBundle:Main:secure',
    ), array(
    '_scheme' => 'https',
    )));
    return $collection;

    Ïðèâåäåííàÿ âûøå êîíôèãóðàöèÿ ìàðøðóòà secure âñåãäà áóäåò èñïîëüçîâàòü ïðîòîêîë HTTPS.

    3.4.

    Êàê çàñòàâèòü ìàðøðóòèçàòîð âñåãäà èñïîëüçîâàòü HTTPS èëè HTTP337

    Symfony Documentation, Âûïóñê 2.0

    Êîãäà ãåíåðèðóåòñÿ URL secure, â ñëó÷àå, åñëè òåêóùàÿ ñõåìà HTTP, òî Symfony àâòîìàòè÷åñêè ñãåíåðèðóåò àáñîëþòíûé URL ñî ñõåìîé HTTPS.
    # Åñëè òåêóùàÿ ñõåìà - HTTPS
    {{ path('secure') }}
    # ñãåíåðèðóåò /secure
    # Åñëè òåêóùàÿ ñõåìà - HTTP
    {{ path('secure') }}
    # ñãåíåðèðóåò https://example.com/secure

    Ïðàâèëî òàêæå ïðèìåíÿåòñÿ è äëÿ âõîäÿùèõ çàïðîñîâ. Åñëè Âû ïîïðîáóåòå ïîëó÷èòü
    äîñòóï ê ðåñóðñó /secure ÷åðåç HTTP, Symfony àâòîìàòè÷åñêè ïåðåíàïðàâèò Âàñ íà
    òîò æå URL, íî ñ èñïîëüçîâàíèåì ñõåìû HTTPS.
    Ïðèâåäåííûå âûøå ïðèìåðû èñïîëüçóþò ïðîòîêîë https äëÿ _scheme, íî òàêæå Âû
    ìîæåòå îãðàíè÷èòü ìàðøðóò íà èñïîëüçîâàíèå òîëüêî http ïðîòîêîëà.

    Ïðèìå÷àíèå:

    Êîìïîíåíò Áåçîïàñíîñòè ïðåäëàãàåò äðóãîé ñïîñîá îãðàíè÷èòü
    èñïîëüçîâàíèå òîëüêî HTTP èëè HTTPs ïðîòîêîëà ïîñðåäñòâîì ïàðàìåòðà
    requires_channel. Ýòîò àëüòåðíàòèâíûé ìåòîä áîëüøå ïîäõîäèò äëÿ çàùèòû îáëàñòè Âàøåãî web-ñàéòà (âñå URL â /admin) èëè êîãäà Âû õîòèòå çàùèòèòü âñå URL,
    îáúÿâëåííûå â ñòîðîííåì ïàêåòå.

    3.5 Êàê îòïðàâëÿòü ýëåêòðîííóþ ïî÷òó
    Ðàññûëêà ýëåêòðîííîé ïî÷òû, ÿâëÿåòñÿ êëàññè÷åñêîé çàäà÷åé äëÿ ëþáîãî âåáïðèëîæåíèÿ, è îäíîé èç òåõ çàäà÷, â êîòîðîé èìåþòñÿ îïðåäåëåííûå ñëîæíîñòè è ïîòåíöèàëüíûå ïðîáëåìû. Âìåñòî èçîáðåòåíèÿ êîëåñà, îäíèì èç ðåøåíèé ïî ðàññûëêå
    ýëåêòðîííîé ïî÷òû, ÿâëÿåòñÿ èñïîëüçîâàíèå ïàêåòà SwiftmailerBundle, êîòîðûé èñïîëüçóåò âîçìîæíîñòè áèáëèîòåêè Swiftmailer .

    Ïðèìå÷àíèå: Íå çàáóäüòå ïîäêëþ÷èòü ïàêåò â ÿäðå, äî íà÷àëà åãî èñïîëüçîâàíèÿ:
    public function registerBundles()
    {
    $bundles = array(
    // ...
    new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
    );
    }

    338

    // ...

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    3.5.1 Íàñòðîéêà

    Äî ìîìåíòà èñïîëüçîâàíèÿ êîìïîíåíòà Swiftmailer, åãî íåîáõîäèìî íàñòðîèòü. Îáÿçàòåëüíûì â íàñòðîéêå êîìïîíåíòà ÿâëÿåòñÿ ïàðàìåòð transport:
    ˆ

    YAML

    # app/config/config.yml
    swiftmailer:
    transport: smtp
    encryption: ssl
    auth_mode: login
    host:
    smtp.gmail.com
    username: âàø_ëîãèí
    password: âàø_ïàðîëü

    ˆ

    XML



    http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftma
    -->
    transport="smtp"
    encryption="ssl"
    auth-mode="login"
    host="smtp.gmail.com"
    username="âàø_ëîãèí"
    password="âàø_ïàðîëü" />

    ˆ

    PHP

    // app/config/config.php
    $container->loadFromExtension('swiftmailer', array(
    'transport' => "smtp",
    'encryption' => "ssl",
    'auth_mode' => "login",
    'host'
    => "smtp.gmail.com",
    'username' => "âàø_ëîãèí",
    'password' => "âàø_ïàðîëü",
    ));

    Áîëüøàÿ ÷àñòü íàñòðîåê Swiftmailer îòâå÷àåò çà òî êàêèì îáðàçîì ñîîáùåíèÿ äîëæíû
    áûòü äîñòàâëåíû.
    Âîçìîæíî èñïîëüçîâàòü ñëåäóþùèå ïàðàìåòðû:
    3.5.

    Êàê îòïðàâëÿòü ýëåêòðîííóþ ïî÷òó

    339

    Symfony Documentation, Âûïóñê 2.0

    ˆ transport (smtp, mail, sendmail, èëè gmail)
    ˆ username
    ˆ password
    ˆ host
    ˆ port
    ˆ encryption (tls, èëè ssl)
    ˆ auth_mode (plain, login, èëè cram-md5)
    ˆ spool

     type (êàêèì îáðàçîì îðãàíèçîâûâàòü î÷åðåäü ñîîáùåíèé, íà äàííûé ìîìåíò
    ïîääåðæèâàåòñÿ òîëüêî ñïîñîá file)

     path (ãäå õðàíèòü ñîîáùåíèÿ)
    ˆ delivery_address (àäðåñ íà êîòîðûé îòïðàâëÿþò ÂÑÅ ïèñüìà)
    ˆ disable_delivery (óñòàíîâêà çíà÷åíèÿ â true îòêëþ÷àåò äîñòàâêó ïèñåì)
    3.5.2 Ðàññûëêà ýëåêòðîííûõ ñîîáùåíèé

    Áèáëèîòåêà Swiftmailer ðàáîòàåò ñ îáúåêòàìè Swift_Message, è çàíèìàåòñÿ èõ ñîçäàíèåì, êîíôèãóðèðîâàíèåì è ðàññûëêîé. Ðàññûëàòåëü (èëè mailer) îòâå÷àåò çà äîñòàâêó
    ñîîáùåíèé è äîñòóïåí ÷åðåç ñåðâèñ mailer.  öåëîì îòïðàâêà ïèñüìà äîñòàòî÷íî ïðîñòà:
    public function indexAction($name)
    {
    // ïîëó÷àåì 'mailer' (îáÿçàòåëåí äëÿ èíèöèàëèçàöèè Swift Mailer)
    $mailer = $this->get('mailer');
    $message = \Swift_Message::newInstance()
    ->setSubject('Hello Email')
    ->setFrom('send@example.com')
    ->setTo('recipient@example.com')
    ->setBody($this->renderView('HelloBundle:Hello:email', array('name' => $name)))
    ;
    $mailer->send($message);
    }

    return $this->render(...);

    Îòìåòèì, ÷òî òåëî ïèñüìà, õðàíèòñÿ â øàáëîíå è îòîáðàæàåòñÿ ñ ïîìîùüþ ìåòîäà
    renderView().

    340

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    Îáúåêò $message ñîäåðæèò ìíîæåñòâî äðóãèõ îïöèé, òàêèõ êàê âëîæåíèÿ, ñîäåðæèìîå
    â ôîðìàòå HTML, è ò.ä. Â äîêóìåíòàöèè ê áèáëèîòåêå Swiftmailer õîðîøî îñâåùåíà
    ãëàâà Ñîçäàíèå ñîîáùåíèé â êîòîðîé ìîæíî íàéòè èíôîðìàöèþ î òîì êàê ñîçäàâàòü
    ñîîáùåíèÿ è îïöèÿõ êîòîðûå ïðè ýòîì äîñòóïíû.

    Ñîâåò: Ðåêîìåíäóåì ïðî÷èòàòü äîêóìåíò  Êàê

    èñïîëüçîâàòü Gmail äëÿ îòïðàâêè

    ýëåêòðîííûõ ïèñåì 

    â êîòîðîì ðàññêàçàíî êàê èñïîëüçîâàòü ïî÷òó gmail â êà÷åñòâå
    òðàíñïîðòà íà ñòàäèè ðàçðàáîòêè.

    3.6 Êàê èñïîëüçîâàòü Gmail äëÿ îòïðàâêè ýëåêòðîííûõ ïèñåì
    Âî âðåìÿ ðàçðàáîòêè, îòïðàâêà ïèñåì ñ ïîìîùüþ ñåðâèñà Gmail ìîæåò îêàçàòüñÿ áîëåå
    ëåãêèì è ïðàêòè÷íûì ðåøåíèåì, íåæåëè èñïîëüçîâàíèå SMTP ñåðâåðà.

    Ñîâåò: Âìåñòî òîãî, ÷òîáû èñïîëüçîâàòü ñâîþ ó÷åòíóþ çàïèñü Gmail, ëó÷øèì ðåøåíèåì áóäåò ñîçäàòü íîâûé àêêàóíò, ïðèìåíèòåëüíî äëÿ ýòèõ öåëåé.

    Â êîíôèãóðàöèîííîì ôàéëå äëÿ ñðåäû ðàçðàáîòêè, èçìåíèòå íàñòðîéêó transport íà
    gmail è çàäàéòå íàñòðîéêè username è password ñîãëàñíî çíà÷åíèÿì èç Gmail:
    ˆ

    YAML

    # app/config/config_dev.yml
    swiftmailer:
    transport: gmail
    username: âàø_gmail_ëîãèí
    password: âàø_gmail_ïàðîëü

    ˆ

    XML



    http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftma
    -->
    transport="gmail"
    username="âàø_gmail_ëîãèí"
    password="âàø_gmail_ïàðîëü" />

    3.6.

    Êàê èñïîëüçîâàòü Gmail äëÿ îòïðàâêè ýëåêòðîííûõ ïèñåì

    341

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    // app/config/config_dev.php
    $container->loadFromExtension('swiftmailer', array(
    'transport' => "gmail",
    'username' => "âàø_gmail_ëîãèí",
    'password' => "âàø_gmail_ïàðîëü",
    ));

    Íà ýòîì íàñòðîéêà Gmail çàêîí÷åíà!

    Ïðèìå÷àíèå: Òðàíñïîðò gmail ÿâëÿåòñÿ âñåãî ëèøü ãîòîâûì øàáëîíîì, êîòîðûé

    èñïîëüçóåò òðàíñïîðò smtp è óñòàíàâëèâàåò ïîëÿ encryption, auth_mode è host äëÿ
    ðàáîòû ñ ïî÷òîé Gmail.

    3.7 Êàê

    ñìîäåëèðîâàòü

    HTTP

    àóòåíòèôèêàöèþ

    â

    Ôóíêöèîíàëüíîì òåñòå
    Åñëè âàøåìó ïðèëîæåíèþ íåîáõîäèìà HTTP àóòåíòèôèêàöèÿ, ïåðåäàéòå èìÿ ïîëüçîâàòåëÿ è ïàðîëü â êà÷åñòâå ïåðåìåííûõ ñåðâåðà â ìåòîä createClient():
    $client = $this->createClient(array(), array(
    'PHP_AUTH_USER' => 'username',
    'PHP_AUTH_PW' => 'pa$$word',
    ));

    Òàêæå ìîæíî äåëàòü ïåðåîïðåäåëåíèÿ â êàæäîì çàïðîñå:
    $client->request('DELETE', '/post/12', array(), array(
    'PHP_AUTH_USER' => 'username',
    'PHP_AUTH_PW' => 'pa$$word',
    ));

    3.8 Êàê òåñòèðîâàòü âçàèìîäåéñòâèå ñ íåñêîëüêèìè
    êëèåíòàìè
    Åñëè òðåáóåòñÿ ñìîäåëèðîâàòü âçàèìîäåéñòâèå ìåæäó ðàçíûìè Êëèåíòàìè (ïðåäñòàâüòå, íàïðèìåð, ÷àò), òî ñîçäàéòå íåñêîëüêî Êëèåíòîâ:
    $harry = static::createClient();
    $sally = static::createClient();

    342

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    $harry->request('POST', '/say/sally/Hello');
    $sally->request('GET', '/messages');
    $this->assertEquals(201, $harry->getResponse()->getStatusCode());
    $this->assertRegExp('/Hello/', $sally->getResponse()->getContent());

    Ýòîò ïîäõîä ðàáîòàåò, çà èñêëþ÷åíèåì òåõ ñëó÷àåâ, êîãäà âàø êîä îáðàáàòûâàåò ãëîáàëüíîå ñîñòîÿíèÿ èëè çàâèñèò îò áèáëèîòåê òðåòüèõ ëèö, êîòîðûå òàêæå åãî èñïîëüçóþò. Äëÿ ïîäîáíûõ ñëó÷àåâ ìîæíî èçîëèðîâàòü êëèåíòîâ:
    $harry = static::createClient();
    $sally = static::createClient();
    $harry->insulate();
    $sally->insulate();
    $harry->request('POST', '/say/sally/Hello');
    $sally->request('GET', '/messages');
    $this->assertEquals(201, $harry->getResponse()->getStatusCode());
    $this->assertRegExp('/Hello/', $sally->getResponse()->getContent());

    Èçîëèðîâàííûå êëèåíòû âûïîëíÿþò ñâîè çàïðîñû â îòäåëüíûõ ÷èñòûõ PHP ïðîöåññàõ,
    ÷òî èñêëþ÷àåò ëþáûå ïîáî÷íûå ýôôåêòû.

    Ñîâåò: Òàê êàê èçîëèðîâàííûé êëèåíò ðàáîòàåò ìåäëåííåå, òî ìîæíî îäíîãî êëèåíòà
    îñòàâèòü âûïîëíÿòüñÿ â ãëàâíîì ïðîöåññå, à îñòàëüíûõ èçîëèðîâàòü.

    3.9 Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå
    Íàñòîÿòåëüíî ðåêîìåíäóåòñÿ ÷òîáû ôóíêöèîíàëüíûé òåñò ïðîâåðÿë òîëüêî Response.
    Íî åñëè ïèøóòñÿ ôóíêöèîíàëüíûå òåñòû, ñëåäÿùèå çà production ñåðâåðàìè, òî âîçìîæíî ó âàñ ïîÿâèòñÿ æåëàíèå íàïèñàòü òåñòû, èñïîëüçóþùèå äàííûå ïðîôèëèðîâùèêà, ò.
    ê. îíè ïîçâîëÿþò ïðîâåðèòü ìíîæåñòâî ïàðàìåòðîâ è îáåñïå÷èòü ñîáëþäåíèå îïðåäåëåííûõ ïîêàçàòåëåé.

    Ïðîôèëèðîâùèê â Symfony2 ñîáèðàåò ìíîæåñòâî äàííûõ ïî êàæäîìó çàïðîñó. Èñïîëüçóéòå èõ äëÿ çàìåðà êîëè÷åñòâà çàïðîñîâ ê ÁÄ, âðåìåíè çàòðà÷åííîãî ôðåéìâîðêîì è
    ò. ä. Íî, ïðåæäå ÷åì ïèñàòü ïðîâåðî÷íûå âûðàæåíèÿ, âñåãäà ñëåäóåò ïðîâåðÿòü äîñòóïíîñòü ïðîôèëèðîâùèêà (ïî-óìîë÷àíèþ ê íåìó åñòü äîñòóï â test îêðóæåíèè):

    3.9.

    Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå

    343

    Symfony Documentation, Âûïóñê 2.0

    class HelloControllerTest extends WebTestCase
    {
    public function testIndex()
    {
    $client = static::createClient();
    $crawler = $client->request('GET', '/hello/Fabien');
    // Íàïèøèòå âûðàæåíèÿ, îòíîñÿùèåñÿ ê Response
    // ...
    // Ïðîâåðÿåò, äîñòóïåí ëè ïðîôèëèðîâùèê
    if ($profile = $client->getProfile()) {
    // ïðîâåðÿåò êîëè÷åñòâî çàïðîñîâ
    $this->assertTrue($profile->get('db')->getQueryCount() < 10);

    }

    }

    }

    // ïðîâåðÿåò âðåìÿ, çàòðà÷åííîå ôðåéìâîðêîì
    $this->assertTrue( $profile->get('timer')->getTime() < 0.5);

    Åñëè òåñò ïðîâàëèòñÿ, îñíîâûâàÿñü íà äàííûõ ïðîôèëèðîâùèêà (íàïðèìåð, ñëèøêîì
    ìíîãî çàïðîñîâ ê ÁÄ), òî ìîæíî âîñïîëüçîâàòüñÿ Âåá Ïðîôèëèðîâùèêîì äëÿ àíàëèçà
    çàïðîñîâ ïîñëå çàâåðøåíèÿ òåñòîâ. Ýòî ëåãêî ñäåëàòü åñëè âñòðîèòü ìåòêó â ñîîáùåíèå
    îá îøèáêå:
    $this->assertTrue(
    $profile->get('db')->getQueryCount() < 30,
    sprintf('Checks that query count is less than 30 (token %s)', $profile->getToken())
    );

    Îñòîðîæíî: Õðàíèëèùå ïðîôèëèðîâùèêà ìîæåò ðàçëè÷àòüñÿ â çàâèñèìîñòè îò
    îêðóæåíèÿ (îñîáåííî åñëè èñïîëüçóåòñÿ õðàíèëèùå SQLite, ÿâëÿþùååñÿ îäíèì èç
    ñêîíôèãóðèðîâàííûõ ïî-óìîë÷àíèþ).
    Ïðèìå÷àíèå:  òåñòàõ èíôîðìàöèÿ ïðîôèëèðîâùèêà äîñòóïíà äàæå â òåõ ñëó÷àÿõ,
    êîãäà êëèåíò èçîëèðîâàí ëèáî èñïîëüçóåòñÿ HTTP ñëîé.

    Ñîâåò: Ïðî÷èòàéòå ïðî API âñòðîåííûõ ñáîðùèêîâ äàííûõ ÷òîáû óçíàòü áîëüøå îá
    èõ èíòåðôåéñàõ.

    344

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    3.10 Êàê èñïîëüçîâàòü Varnish äëÿ óñêîðåíèÿ ðàáîòû
    ñàéòà
    Òàê êàê êåø Symfony2 èñïîëüçóåò ñòàíäàðòíûå HTTP-çàãîëîâêè êåøà, Îáðàòíûé ïðîêñè Symfony2 ìîæåò áûòü ëåãêî çàìåíåí ëþáûì äðóãèì reverse proxy. Varnish - ýòî ìîùíûé HTTP-àêñåëåðàòîð ñ îòêðûûòìè èñõîäíûìè êîäàìè, êîòîðûé ïîçâîëÿåò áûñòðî
    îòäàâàòü çàêåøèðîâàííûé êîíòåíò è ïîçâîëÿåò èñïîëüçîâàòü Edge Side Includes .
    3.10.1 Íàñòðîéêà

    Êàê ìû âèäåëè ðàíüøå, Symfony2 ìîæåò îïðåäåëèòü, èñïîëüçóåòñÿ ëè reverse proxy, êîòîðûé ïîíèìàåò ESI, èëè íåò. Ýòî ðàáîòàåò ¾èç êîðîáêè¿, êîãäà âû ïîëüçóåòåñü reverse
    proxy â Symfony2, íî äëÿ ðàáîòû ñ Varnish íóæíà ñïåöèàëüíàÿ íàñòðîéêà. Áëàãîäàðÿ ñòàíäàðòó, ðàçðàáîòàííîìó Akama (`Edge Architecture`_), ñîâåòû èç ýòîé ãëàâû
    ìîãóò áûòü ïîëåçíû äàæå åñëè âû íå èñïîëüçóåòå Symfony2.

    Ïðèìå÷àíèå: Varnish ïîääåðæèâàåò òîëüêî àòðèáóò src äëÿ òåãîâ ESI (àòðèáóòû
    onerror è alt èãíîðèðóþòñÿ).

    Äëÿ íà÷àëà íàñòðîéòå Varnish òàêèì îáðàçîì, ÷òîáû îí îïîâåùàë î ïîääåðæêå ESI ÷åðåç çàãîëîâîê Surrogate-Capability òåõ çàïðîñîâ, êîòîðûå ïåðåíàïðàâëÿþòñÿ backendïðèëîæåíèþ:
    sub vcl_recv {
    set req.http.Surrogate-Capability = "abc=ESI/1.0";
    }

    Çàòåì, îïòèìèçèðóéòå Varnish òàêèì îáðàçîì, ÷òîáû îí ïàðñèë ñîäåðæèìîå îòâåòà, êîãäà â íåì ïðèñóòñòâóåò õîòÿ áû îäèí òåã ESI. Ýòîãî ìîæíî äîáèòüñÿ, ïðîâåðèâ íàëè÷èå
    îòâåòà Surrogate-Control, êîòîðûé äîáàâëÿåòñÿ àâòîìàòè÷åñêè Symfony2:
    sub vcl_fetch {
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
    unset beresp.http.Surrogate-Control;
    esi;
    }
    }

    Îñòîðîæíî: Íå èñïîëüçóéòå ñæàòèå ñ ESI, òàê êàê Varnish íå ñìîæåò ïðîïàðñèòü

    ñîäåðæèìîå îòâåòà. Åñëè âû õîòèòå èñïîëüçîâàòü ñæàòèå, óñòàíîâèòå âåá-ñåðâåð ïåðåä Varnish, êîòîðûé áû îðãàíèçîâûâàë ñæàòèå îòâåòà.

    3.10.

    Êàê èñïîëüçîâàòü Varnish äëÿ óñêîðåíèÿ ðàáîòû ñàéòà

    345

    Symfony Documentation, Âûïóñê 2.0

    3.10.2 Àííóëèðîâàíèå êåøà

    Ïî èäåå, âàì íèêîãäà íå ïîòðåáóåòñÿ àííóëèðîâàíèå êåøà, ïîòîìó ÷òî ýòî óæå ó÷èòûâàåòñÿ â HTTP (ñì. Î÷èñòêà (àííóëèðîâàíèå) êýøà ).
    Îäíàêî, Varnish ìîæåò áûòü íàñòðîåí òàê, ÷òîáû ìîã ïðèíèìàòü ñïåöèàëüíûé ìåòîä
    HTTP - PURGE - êîòîðûé ìîæåò àííóëèðîâàòü êåø äëÿ âõîäÿùèõ çàïðîñîâ:
    sub vcl_hit {
    if (req.request == "PURGE") {
    set obj.ttl = 0s;
    error 200 "Purged";
    }
    }
    sub vcl_miss {
    if (req.request == "PURGE") {
    error 404 "Not purged";
    }
    }

    Îñòîðîæíî: Ìû äîëæíû îãðàíè÷èòü äîñòóï ê HTTP-ìåòîäó PURGE, ÷òîáû èçáå-

    æàòü åãî èñïîëüçîâàíèå äðóãèìè ëþäüìè;

    3.11 Âíåäðåíèå ïåðåìåííûõ âî âñå øàáëîíû (ò.í.
    Ãëîáàëüíûå ïåðåìåííûå)
    Èíîãäà âàì ìîæåò ïîòðåáîâàòüñÿ, ÷òîáû íåêîòîðàÿ ïåðåìåííàÿ áûëà äîñòóïíà âî âñåõ âàøèõ øàáëîíàõ. Ýòîãî ìîæíî äîñòè÷ü ïðè ïîìîùè âàøåãî ôàéëà
    app/config/config.yml:
    # app/config/config.yml
    twig:
    # ...
    globals:
    ga_tracking: UA-xxxxx-x

    Òåïåðü, ïåðåìåííàÿ ga_tracking áóäåò äîñòóïíà âî âñåõ øàáëîíàõ Twig:

    Our google tracking code is: {{ ga_tracking }}



    Òàê ïðîñòî! Âû òàêæå ìîæåòå ïîëó÷èòü äîñòóï ê ñèñòåìíûì ïàðàìåòðàì  Ïàðàìåòðû
    ñëóæáû , êîòîðûå ïîçâîëÿò âàì èçîëèðîâàòü èëè ïîâòîðíî èñïîëüçîâàòü çíà÷åíèå:

    346

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    ; app/config/parameters.yml
    [parameters]
    ga_tracking: UA-xxxxx-x
    # app/config/config.yml
    twig:
    globals:
    ga_tracking: %ga_tracking%

    The same variable is available exactly as before.
    3.11.1 Áîëåå ñëîæíûå ãëîáàëüíûå ïåðåìåííûå

    Åñëè ãëîáàëüíàÿ ïåðåìåííàÿ, êîòîðóþ âàì íàäî èñïîëüçîâàòü, áîëåå ñëîæíàÿ - ê ïðèìåðó, îáúåêò - òîãäà âû íå ñìîæåòå âîñïîëüçîâàòüñÿ ïðèâåä¼ííûì âûøå ìåòîäîì. Âìåñòî
    ýòîãî âàì íóæíî ñîçäàòü Ðàñøèðåíèå Twig è âîçâðàùàòü ãëîáàëüíóþ ïåðåìåííóþ ñðåäè
    çíà÷åíèé ìåòîäà getGlobals.

    3.12 Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig
    Íå ñìîòðÿ íà òî, ÷òî Symfony2 ïî óìîë÷àíèþ èñïîëüçóåò øàáëîíû Twig â ÷àñòâå øàáëîííîãî äâèæêà, âû ìîæåòå èñïîëüçîâàòü PHP øàáëîíû, åñëè çàõîòèòå. Îáà øàáëîííûõ äâèæêà îäèíàêîâî ïîääåðæèâàþòñÿ â Symfony2. Symfony2 òàêæå äîáàâëÿåò ê PHP
    øàáëîíàì íåñêîëüêî óäîáíûõ ôè÷, ÷òîáû ñäåëàòü èõ áîëåå ìîùíûìè.
    3.12.1 Îòîáðàæåíèå PHP øàáëîíîâ

    Åñëè âû õîòèòå èñïîëüçîâàòü PHP øàáëîíû, âî-ïåðâûõ, óáåäèòåñü ÷òî âû èõ àêòèâèðîâàëè â íàñòðîéêàõ ïðèëîæåíèÿ:
    ˆ

    YAML

    # app/config/config.yml
    framework:
    # ...
    templating:
    { engines: ['twig', 'php'] }

    ˆ

    XML





    3.12.

    Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig

    347

    Symfony Documentation, Âûïóñê 2.0






    ˆ

    PHP

    $container->loadFromExtension('framework', array(
    // ...
    'templating'
    => array(
    'engines' => array('twig', 'php'),
    ),
    ));

    Òåïåðü âû ìîæåòå èñïîëüçîâàòü PHP øàáëîíû âçàìåí Twig, ïðîñòî óêàçûâàÿ ðàñøèðåíèå .php äëÿ âàøèõ øàáëîíîâ, à íå .twig. Êîíòðîëëåð èç ïðèìåðà íèæå îòîáðàæàåò
    øàáëîí index.html.php:
    // src/Acme/HelloBundle/Controller/HelloController.php
    public function indexAction($name)
    {
    return $this->render('AcmeHelloBundle:Hello:index.html.php', array('name' => $name));
    }

    3.12.2 Äåêîðèðîâàíèå øàáëîíîâ

    ×àùå âñåãî, øàáëîíû èñïîëüçóþò íåêîòîðûå îáùèå ýëåìåíòû, íàïðèìåð âñåì èçâåñòíûå
    header è footer.  Symfony2 ýòîò âîïðîñ ðåøàåòñÿ íåñêîëüêî èíà÷å - øàáëîí ìîæåò áûòü
    äåêîðèðîâàí äðóãèì øàáëîíîì.
    Øàáëîí index.html.php áóäåò äåêîðèðîâàòüñÿ øàáëîíîì layout.html.php áëàãîäàðÿ
    âûçîâó ìåòîäà extend():

    extend('AcmeHelloBundle::layout.html.php') ?>
    Hello !

    Íîòàöèÿ AcmeHelloBundle::layout.html.php âûãëÿäèò çíàêîìî, íå òàê ëè? Ýòî òàêàÿ æå íîòàöèÿ, êîòîðàÿ èñïîëüçóåòñÿ äëÿ ññûëêè íà øàáëîí. ×àñòü :: îçíà÷àåò, ÷òî
    ýëåìåíò êîíòðîëëåð, òàêèì îáðàçîì ñîîòâåòñòâóþùèé ôàéë ðàñïëîæåí íàïðÿìóþ â
    äèðåêòîðèè views/.
    Òåïåðü äàâàéòå âçãëÿíåì íà ôàéë layout.html.php:

    348

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0


    extend('::base.html.php') ?>

    Hello Application


    output('_content') ?>

    Äåêîðèðóþùèé øàáëîí (layout) â ñâîþ î÷åðåäü äåêîðèðîâàí äðóãèì øàáëîíîì
    (::base.html.php). Symfony2 ïîääåðæèâàåò ìíîæåñòâåííûå óðîâíè äåêîðèðîâàíèÿ:
    layout ìîæåò áûòü äåêîðèðîâàí äðóãèì øàáëîíîì áîëåå âûñîêîãî óðîâíÿ. Êîãäà ÷àñòü bundle â íàèìåíîâàíèè øàáëîíà ïóñòà, îí èùåòñÿ â äèðåêòîðèè
    app/Resources/views/. Îíà ñîäåðæèò ãëîáàëüíûå øàáëîíû óðîâíÿ ïðèëîæåíèÿ:





    <?php $view['slots']->output('title', 'Hello Application') ?>


    output('_content') ?>



    Äëÿ îáîèõ øàáëîíîâ, âûðàæåíèå $view['slots']->output('_content') çàìåíÿåòñÿ
    êîíòåíòîì äî÷åðíåãî øàáëîíà: index.html.php è layout.html.php ñîîòâåòñòâåííî (î
    ñëîòàõ ïîäðîáíåå ïîãîâîðèì â ñëåäóþùåé ñåêöèè).
    Êàê âû ìîæåòå âèäåòü, Symfony2 ïðåäîñòàâëÿåò ìåòîäû ïîñðåäñòâîì îáúåêòà $view. Â
    øàáëîíå ïåðåìåííàÿ $view âñåãäà äîñòóïíà è ïðåäñòàâëÿåò ñîáîé ñïåöèàëüíûé îáúåêò,
    êîòîðûå ïðåäîñòàâëÿåò ïàêåò ìåòîäîâ, êîòîðûå ñîáñòâåííî è êðóòÿò âèíòèêè â äâèæêå
    øàáëîíèçàòîðà.
    3.12.3 Ðàáîòàåì ñî Ñëîòàìè

    Ñëîò - ýòî íåêîòîðûé êóñî÷åê êîäà, îïðåäåë¼ííûé â øàáëîíå è êîòîðûé ìîæíî ïîâòîðíî
    èñïîëüçîâàòü â ëþáîì äåêîðèðóþùåì øàáëîíå.  øàáëîíå index.html.php îïðåäåë¼í
    ñëîò title:

    extend('AcmeHelloBundle::layout.html.php') ?>
    set('title', 'Hello World Application') ?>
    Hello !
    3.12.

    Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig

    349

    Symfony Documentation, Âûïóñê 2.0

    Áàçîâûé øàáëîí óæå èìååò êîä äëÿ âûâîäà çàãîëîâêà:



    <?php $view['slots']->output('title', 'Hello Application') ?>


    Ìåòîä output() âñòàâëÿåò êîíòåíò ñëîòà è îïöèîíàëüíî ïðèíèìàåò çíà÷åíèå ïî óìîë÷àíèþ, êîòîðîå áóäåò èñïîëüçîâàíî, åñëè ñëîò íå áóäåò îïðåäåë¼í. _content - ýòî îñîáûé
    ñëîò, êîòîðûé ñîäåðæèò ðåíäåð äî÷åðíåãî øàáëîíà.
    Äëÿ áîëüøèõ ñëîòîâ ìîæíî èñïîëüçîâàòü ðàñøèðåííûé ñèíòàêñèñ:
    start('title') ?>
    Some large amount of HTML
    stop() ?>

    3.12.4 Âêëþ÷åíèå äðóãèõ øàáëîíîâ

    Íàèëó÷øèì ñïîñîáîì ñäåëàòü äîñòóïíûì â äðóãèõ øàáëîíàõ íåêîòîðûé êóñî÷åê êîäà
    - ýòî âêëþ÷èòü åãî â äðóãèå øàáëîðíû.
    Ñîçäàäèì øàáëîí hello.html.php:

    Hello !

    È èçìåíèì øàáëîí index.html.php òàêèì îáðàçîì ÷òîáû îí åãî ïîäêëþ÷àë:

    extend('AcmeHelloBundle::layout.html.php') ?>
    render('AcmeHelloBundle:Hello:hello.html.php', array('name' => $name)) ?>

    Ìåòîä render() âû÷èñëÿåò è âîçâðàùàåò çíà÷åíèå äðóãîãî øàáëîíà (ýòî òàêîé æå ìåòîä, êîòîðûé èñïîëüçóåòñÿ â êîíòðîëëåðå).
    3.12.5 Âñòðàèâàíèå Êîíòðîëëåðîâ

    ×òî, åñëè âû çàõîòèòå âñòðîèòü ðåçóëüòàò âûïîëíåíèÿ äðóãîãî êîíòðîëëåðà â øàáëîí?
    Ýòî î÷åíü óäîáíî ïðè ðàáîòå ñ Ajax, èëè æå êîãäà âñòðàèâàåìûé øàáëîí òðåáóåò íåêîòîðûå ïåðåìåííûå, íå äîñòóïíûå â ãëàâíîì øàáëîíå.
    Åñëè âû ñîçäàäèòå äåéñòâèå fancy è çàõîòèòå âêëþ÷èòü åãî â øàáëîí index.html.php,
    ïðîñòî èñïîëüçóéòå òàêîé êîä:

    350

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0


    render('AcmeHelloBundle:Hello:fancy', array('name' => $name, 'color

    Ñòðîêà AcmeHelloBundle:Hello:fancy ññûëàåòñÿ íà äåéñòâèå fancy êîíòðîëëåðà Hello:
    // src/Acme/HelloBundle/Controller/HelloController.php
    class HelloController extends Controller
    {
    public function fancyAction($name, $color)
    {
    // create some object, based on the $color variable
    $object = ...;
    }
    }

    return $this->render('AcmeHelloBundle:Hello:fancy.html.php', array('name' => $name, 'obj

    // ...

    Íî ãäå æå îïðåäåë¼í ýëåìåíò ìàññèâà $view['actions']? Êàê è $view['slots'], çäåñü
    âûçûâàåòñÿ õåëïåð è â ñëåäóþùåé ñåêöèè îá ýòîì áóäåò ÷óòü ïîäðîáíåå.
    3.12.6 Èñïîëüçîâàíèå õåëïåðîâ â øàáëîíàõ

    Ñèñòåìà øàáëîíîâ Symfony2 ìîæåò áûòü ëåãêî è ïðîñòî ïðè ïîìîùè õåëïåðîâ. Õåëïåðû ýòî PHP îáúåêòû, êîòîðûå ïðåäîñòàâëÿþò óäîáíûå ôè÷è â êîíòåêñòå øàáëîíîâ.
    actions è slots - ýòî äâà õåëïåðà èç ÷èñëà âñòðîåííûõ â Symfony2.
    Ñîçäàíèå ññûëîê ìåæäó ñòðàíèöàìè

    Ãîâîðÿ î âåá-ïðèëîæåíèÿõ, ñîçäàíèå ññûëîê ìåæäó ñòðàíèöàìè - ýòî êðàéíå íåîáõîäèìàÿ ôóíêöèÿ. Âìåñòî òîãî, ÷òîáû õîðäêîäèòü URLû â øàáëîíàõ, íóæíî èñïîëüçîâàòü
    õåëïåð router, êîòîðûé çíàåò êàê ãåíåðèðîâàòü URL, îñíîâûâàÿñü íà êîíôèãóðàöèþ
    ìàðøðóòèçàòîðà. Åñëè èñïîëüçîâàòü ýòîò ïîäõîä, ëþáîé URL ìîæåò áûòü ëåãêî îáíîâë¼í ïðè ïîìîùè èçìåíåíèÿ êîíôèãóðàöèè:

    Greet Thomas!


    Ìåòîä generate() ïðèíèìàåò èìÿ ìàðøðóòà è ìàññèâ ïàðàìåòðîâ â êà÷åñòâå àðãóìåíòîâ. Èìÿ ìàðøðóòà - ýòî êëþ÷, ïî êîòîðîìó îïðåäåëÿåòñÿ ìàðøðóò, à ïàðàìåòðû - ýòî
    çíà÷åíèÿ ïëýéñõîëäåðîâ èç øàáëîíà ìàðøðóòà:
    3.12.

    Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig

    351

    Symfony Documentation, Âûïóñê 2.0

    # src/Acme/HelloBundle/Resources/config/routing.yml
    hello: # The route name
    pattern: /hello/{name}
    defaults: { _controller: AcmeHelloBundle:Hello:index }

    Ðàáîòà ñ ðåñóðñàìè: êàðòèíêè, JavaScript, CSS

    ×åì áû áûë èíòåðíåò áåç êàðòèíîê, äæàâàñêðèïòîâ è ñòèëåé? Symfony2 ïðåäîñòàâëÿåò
    âàì òàã assets äëÿ ðàáîòû ñ íèìè:

    getUrl('images/logo.png') ?>" />

    Ãëàâíîé îáÿçàííîñòüþ õåëïåðà assets - ñäåëàòü âàøå ïðèëîæåíèå áîëåå ïîðòèðóåìûì.
    Áëàãîäàðÿ ýòîìó õåëïåðó âû ìîæåòå ïåðåìåùàòü êîðåíü âàøåãî ïðèëîæåíèÿ âíóòðè
    web root íå ìåíÿÿ íè÷åãî ó êîäå øàáëîíîâ.
    3.12.7 Ýêðàíèðîâàíèå

    Ïðè èñïîëüçîâàíèè PHP øàáëîíîâ íåîáõîäèìî ýêðàíèðîâàòü âñå ïåðåìåííûå:
    escape($var) ?>

    Ïî óìîë÷àíèþ, ìåòîä escape() ïîëàãàåò, ÷òî ïåðåìåííàÿ âûâîäèòñÿ â êîíòåêñòå HTML.
    Âòîðîé àðãóìåíò ìåòîäà ïîçâîëÿåò èçìåíèòü êîíòåêñò. Íàïðèìåð, âûâîäÿ ÷òî-òî â
    JavaScript, èñïîëüçóéòå js êîíòåêñò:
    escape($var, 'js') ?>

    3.13 Êàê èñïîëüçîâàòü Monolog äëÿ æóðíàëèðîâàíèÿ
    Áèáëèîòåêà Monolog ïðåäíàçíà÷åíà äëÿ âåäåíèÿ æóðíàëîâ â PHP 5.3 è èñïîëüçóåòñÿ
    Symfony2. ż ïðîòîòèïîì ïîñëóæèëà áèáëèîòåêà LogBook â Python.
    3.13.1 Èñïîëüçîâàíèå

     Ìîíîëîãå, êàæäûé ýëåìåíò æóðíàëèðîâàíèÿ(ëîããåð) îïðåäåëÿåò ñâîé êàíàë æóðíàëèðîâàíèÿ(logger). Êàæäûé êàíàë èìååò ñòåê îáðàáîò÷èêîâ êîòîðûå ïèøóò æóðíàë
    (ëîã) (ïðè÷åì îáðàáîò÷èêè ìîãóò áûòü îáùèìè).

    352

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    Ñîâåò: Ïðè óñòàíîâêå ëîããåðà â ñåðâèñ, ìîæíî èñïîëüçîâàòü ñâîé

    êàíàë

    ñìàòðèâàòü êàêàÿ ÷àñòü ïðèëîæåíèÿ îñòàâèëà ñîîáùåíèå â æóðíàëå.

    è ëåãêî ïðî-

    Ïðîñòåéøèì îáðàáîò÷èêîì ÿâëÿåòñÿ StreamHandler, êîòîðûé ïèøåò æóðíàë â ïîòîê
    (ïî-óìîë÷àíèþ â app/logs/prod.log â production ñðåäå è app/logs/dev.log â ñðåäå
    ðàçðàáîòêè).
     ñîñòàâ Monolog òàêæå âõîäèò ìîùíûé îáðàáîò÷èê, ïðåäíàçíà÷åííûé äëÿ æóðíàëèðîâàíèÿ â production ñðåäå: FingersCrossedHandler. Îí ïîçâîëÿåò õðàíèòü ñîîáùåíèÿ
    â áóôåðå è çàïèñûâàòü èõ â æóðíàë òîëüêî ïðè óñëîâèè òîãî, ÷òî îíî äîõîäèò äî óðîâíÿ êîíòðîëëåðà (ERROR â êîíôèãóðàöèè ñòàíäàðòíîé ðåäàêöèè) ïåðåíàïðàâëÿÿ èõ ê
    äðóãîìó îáðàáîò÷èêó.
    ×òîáû çàïèñàòü ñîîáùåíèå â æóðíàë, ïðîñòî ïîëó÷èòå äîñòóï ê ñåðâèñó ëîããåðà èç
    êîíòåéíåðà â âàøåì êîíòðîëëåðå:
    $logger = $this->get('logger');
    $logger->info('We just go the logger');
    $logger->err('An error occured');

    Èñïîëüçîâàíèå íåñêîëüêèõ îáðàáîò÷èêîâ

    Ëîãåð èñïîëüçóåò ñòåê îáðàáîò÷èêîâ êîòîðûå âûçûâàþòñÿ ïîñëåäîâàòåëüíî. Äàííàÿ
    îñîáåííîñòü ïîçâîëÿåò ëåãêî çàïèñûâàòü ñîîáùåíèÿ â æóðíàë ðàçëè÷íûìè ñïîñîáàìè.
    ˆ

    YAML

    monolog:
    handlers:
    syslog:
    type: stream
    path: /var/log/symfony.log
    level: error
    main:
    type: fingerscrossed
    action_level: warning
    handler: file
    file:
    type: stream
    level: debug

    ˆ

    XML

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/di
    3.13.

    Êàê èñïîëüçîâàòü Monolog äëÿ æóðíàëèðîâàíèÿ

    353

    Symfony Documentation, Âûïóñê 2.0

    http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic

    name="syslog"
    type="stream"
    path="/var/log/symfony.log"
    level="error"
    />
    name="main"
    type="fingerscrossed"
    action-level="warning"
    handler="file"
    />
    name="file"
    type="stream"
    level="debug"
    />



    Êîíôèãóðàöèÿ âûøå, îïðåäåëÿåò ñòåê îáðàáîò÷èêîâ êîòîðûå áóäóò âûçâàíû â ïîðÿäêå
    â êîòîðîì îíè îáúÿâëåíû.

    Ñîâåò: Îáðàáîò÷èê le íå áóäåò âêëþ÷åí â ñòåê, òàê êàê îí ñàì èñïîëüçóåòñÿ â
    êà÷åñòâå âëîæåííîãî îáðàáîò÷èêà â production ñðåäå.

    Ïðèìå÷àíèå: Åñëè ó âàñ ïîÿâèòüñÿ æåëàíèå èçìåíèòü íàñòðîéêè MonologBundle â
    äðóãîì ôàéëå íàñòðîåê, òî íåîáõîäèìî áóäåò ïîëíîñòüþ ïåðåîïðåäåëèòü âåñü ñòåê. Îí
    íå ìîæåò áûòü îáúåäèíåí ñ òåêóùèìè íàñòðîéêàìè, ò.ê. â ðåçóëüòàòå îáúåäèíåíèÿ íàñòðîåê íåâîçìîæíî óïðàâëÿòü ïîðÿäêîì âûçîâà îáðàáîò÷èêîâ.

    Èçìåíåíèå ôîðìàòèðîâàíèÿ

    Îáðàáîò÷èê èñïîëüçóåò Formatter äëÿ ôîðìàòèðîâàíèÿ çàïèñåé, ïåðåä çàïèñüþ èõ â æóðíàë. Âñå îáðàáîò÷èêè Monolog ïî-óìîë÷àíèþ èñïîëüçóþò ýêçåìïëÿð Monolog\Formatter\LineFormatter, íî åãî ëåãêî çàìåíèòü ñâîèì ñîáñòâåííûì. Âàø ñîáñòâåííûé ôîðìàòèðîâùèê äîëæåí èñïîëüçîâàòü èíòåðôåéñ
    Monolog\Formatter\LineFormatterInterface.
    ˆ
    354

    YAML

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    services:
    my_formatter:
    class: Monolog\Formatter\JsonFormatter
    monolog:
    handlers:
    file:
    type: stream
    level: debug
    formatter: my_formatter

    ˆ

    XML

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/di
    http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic




    name="file"
    type="stream"
    level="debug"
    formatter="my_formatter"
    />



    3.13.2 Äîïîëíèòåëüíàÿ èíôîðìàöèÿ â ñîîáùåíèÿõ æóðíàëà

    Monolog ïîçâîëÿåò äîáàâëÿòü äîïîëíèòåëüíûå äàííûå â ñîîáùåíèÿ ïåðåä èõ çàïèñüþ
    â æóðíàë. Ïðîöåññîð ìîæåò áûòü ïðèìåíåí êàê êî âñåìó ñòåêó òàê è ê êàêîìó-ëèáî
    îïðåäåëåííîìó îáðàáîò÷èêó èç åãî ñîñòàâà.
    Ïðîöåññîð - ýòî ñåðâèñ ïîëó÷àþùèé çàïèñü â êà÷åñòâå ïåðâîãî àðãóìåíòà è ëîããåð èëè
    îáðàáîò÷èê â êà÷åñòâå âòîðîãî, â çàâèñèìîñòè îò òîãî íà êàêîì óðîâíå îí âûçûâàåòñÿ.
    ˆ

    YAML

    services:
    my_processor:
    class: Monolog\Processor\WebProcessor
    monolog:
    handlers:
    3.13.

    Êàê èñïîëüçîâàòü Monolog äëÿ æóðíàëèðîâàíèÿ

    355

    Symfony Documentation, Âûïóñê 2.0

    file:
    type: stream
    level: debug
    processors:
    - Acme\MyBundle\MyProcessor::process
    processors:
    - @my_processor

    ˆ

    XML

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/di
    http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic




    name="file"
    type="stream"
    level="debug"
    formatter="my_formatter"
    >






    Ñîâåò: Åñëè âàøåìó ïðîöåññîðó òðåáóþòñÿ çàâèñèìîñòè, òî ìîæíî îáúÿâèòü ñåðâèñ

    è ðåàëèçîâàòü ìåòîä __invoke â êëàññå, ñ òåì ÷òîáû ñäåëàòü åãî âûçûâàåìûì. Ïîñëå
    èçìåíåíèé ïðîöåññîð ìîæíî äîáàâèòü â ñòåê.

    3.14 Êàê àâòîìàòè÷åñêè çàãðóæàòü êëàññû
     ñëó÷àÿõ êîãäà èñïîëüçóþòñÿ íåîïðåäåëåííûå êëàññû, PHP èñïîëüçóåò ìåõàíèçì àâòîçàãðóçêè êîòîðîìó ïîðó÷àåò çàãðóçêó ôàéëà îïèñûâàþùåãî êëàññ. Ñ Symfony2 ïîñòàâëÿåòñÿ óíèâåðñàëüíûé àâòîçàãðóç÷èê, êîòîðûé ìîæåò çàãðóæàòü êëàññû èç ôàéëîâ,
    êîòîðûå ðåàëèçóþò îäíî èç ñëåäóþùèõ ñîãëàøåíèé:
    ˆ Òåõíè÷åñêèå ñòàíäàðòû âçàèìîäåéñòâèÿ äëÿ èìåí ïðîñòðàíñòâ è êëàññîâ PHP 5.3;
    356

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    ˆ Ñîãëàøåíèÿ èìåíîâàíèÿ êëàññîâ â PEAR.
    Åñëè âàøè êëàññû è áèáëèîòåêè 3-õ ëèö êîòîðûìè âû ïîëüçóåòåñü â ïðîåêòå ñëåäóþò
    äàííûì ñòàíäàðòàì, àâòîçàãðóç÷èê Symfony2 åäèíñòâåííûé àâòîçàãðóç÷èê êîòîðûé âàì
    êîãäà-ëèáî ïîíàäîáèòüñÿ.
    3.14.1 Èñïîëüçîâàíèå

    Äîáàâëåíî â âåðñèè 2.1: The useIncludePath method was added in Symfony 2.1. Ðåãèñòðàöèÿ êëàññà Symfony\Component\ClassLoader\UniversalClassLoader àâòîçàãðóçêè
    ïðîñòà:
    require_once '/path/to/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
    use Symfony\Component\ClassLoader\UniversalClassLoader;
    $loader = new UniversalClassLoader();
    //  êà÷åñòâå ïîñëåäíåé èíñòàíöèè èùåì â include_path.
    $loader->useIncludePath(true);
    $loader->register();

    Ñ öåëüþ óëó÷øåíèÿ áûñòðîäåéñòâèÿ - ïóòè ê êëàññàì ìîãóò êýøèðîâàòüñÿ â ïàìÿòè ïðè ïîìîùè APC - äëÿ ýòîãî íåîáõîäèìî ðåãèñòðèðîâàòü êëàññ
    Symfony\Component\ClassLoader\ApcUniversalClassLoader:
    require_once '/path/to/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
    require_once '/path/to/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
    use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
    $loader = new ApcUniversalClassLoader('apc.prefix.');
    $loader->register();

    Àâòîçàãðóç÷èê ïîëåçåí òîëüêî ïðè óñëîâèè òîãî, ÷òî âû äîáàâèòå íåñêîëüêî áèáëèîòåê
    äëÿ àâòîçàãðóçêè.

    Ïðèìå÷àíèå:

    Àâòîçàãðóç÷èê àâòîìàòè÷åñêè ðåãèñòðèðóåòñÿ ïðèëîæåíèåì íà
    Symfony2 (ñì. app/autoload.php).
    Åñëè
    æàòü

    3.14.

    êëàññû
    èñïîëüçóþò

    êîòîðûå
    òðåáóåòñÿ
    ïðîñòðàíñòâà
    èì¼í,

    Êàê àâòîìàòè÷åñêè çàãðóæàòü êëàññû

    àâòîìàòè÷åñêè
    ïðèìåíÿéòå

    çàãðóìåòîäû

    357

    Symfony Documentation, Âûïóñê 2.0

    :method:`Symfony\\Component\\ClassLoader\\UniversalClassLoader::registerNamespace`
    èëè :method:`Symfony\\Component\\ClassLoader\\UniversalClassLoader::registerNamespa
    $loader->registerNamespace('Symfony', __DIR__.'/vendor/symfony/src');
    $loader->registerNamespaces(array(
    'Symfony' => __DIR__.'/../vendor/symfony/src',
    'Monolog' => __DIR__.'/../vendor/monolog/src',
    ));
    $loader->register();

    Äëÿ êëàññîâ êîòîðûå èñïîëüçóþò ñîãëàøåíèÿ îá èìåíîâàíèè â ñòèëå PEAR, èñïîëüçóéòå ìåòîä :method:`Symfony\\Component\\ClassLoader\\UniversalClassLoader::registerPrex
    èëè :method:`Symfony\\Component\\ClassLoader\\UniversalClassLoader::registerPrexes`:
    $loader->registerPrefix('Twig_', __DIR__.'/vendor/twig/lib');
    $loader->registerPrefixes(array(
    'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes',
    'Twig_' => __DIR__.'/vendor/twig/lib',
    ));
    $loader->register();

    Ïðèìå÷àíèå: Íåêîòîðûå áèáëèîòåêè òðåáóþò ÷òîáû èõ êîðíåâîé êàòàëîã òàêæå áûë
    âêëþ÷åí â êà÷åñòâå ïóòè äëÿ ïîèñêà PHP (set_include_path()).

    Êëàññû íàõîäÿùèåñÿ â ïîäïðîñòðàíñòâàõ èì¼í èëè â ñóá-èåðàðõèè êëàññîâ PEAR ìîæíî ëåãêî ñãðóïïèðîâàòü â ïîäìíîæåñòâà, êîòîðûå ìîæíî èñïîëüçîâàòü â áîëüøèõ ïðîåêòàõ:
    $loader->registerNamespaces(array(
    'Doctrine\\Common'
    =>
    'Doctrine\\DBAL\\Migrations' =>
    'Doctrine\\DBAL'
    =>
    'Doctrine'
    =>
    ));

    __DIR__.'/vendor/doctrine-common/lib',
    __DIR__.'/vendor/doctrine-migrations/lib',
    __DIR__.'/vendor/doctrine-dbal/lib',
    __DIR__.'/vendor/doctrine/lib',

    $loader->register();

    Â

    ïðèìåðå, ïðè ïîïûòêå èñïîëüçîâàíèÿ êëàññà â ïðîñòðàíñòâå èìåí
    Doctrine\Common èëè åãî ïîòîìêîâ, àâòîçàãðóç÷èê â ïåðâóþ î÷åðåäü ïðîñìîòðèò â ïîèñêàõ êëàññà êàòàëîã doctrine-common, è â ïîñëåäíþþ î÷åðåäü êàòàëîã Doctrine (êî358

    ýòîì

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    òîðûé ñêîíôèãóðèðîâàí ïîñëåäíèì).  äàííîì ñëó÷àå ïîðÿäîê ðåãèñòðàöèè êëàññîâ
    âàæåí.

    3.15 Êàê èñêàòü ôàéëû
    Ñ ïîìîùüþ êîìïîíåíòà :namespace:`Symfony\\Component\\Finder` ìîæíî ëåãêî
    è áûñòðî íàõîäèòü íåîáõîäèìûå ôàéëû è êàòàëîãè.
    3.15.1 Èñïîëüçîâàíèå

    Êëàññ Symfony\Component\Finder\Finder ïðîèçâîäèò ïîèñê ôàéëîâ è/èëè êàòàëîãîâ:
    use Symfony\Component\Finder\Finder;
    $finder = new Finder();
    $finder->files()->in(__DIR__);
    foreach ($finder as $file) {
    print $file->getRealpath()."\n";
    }

    Îáúåêò $file ÿâëÿåòñÿ ýêçåìïëÿðîì êëàññà :phpclass:`SplFileInfo`. Êîä âûøå ïå÷àòàåò èìåíà âñåõ ôàéëîâ â òåêóùåì êàòàëîãå ðåêóðñèâíî. Êëàññ Finder èñïîëüçóåò ñâîáîäíûé èíòåðôåéñ, òàê ÷òî âñå ìåòîäû âîçâðàùàþò òèï äàííûõ Finder.

    Ñîâåò: Ýêçåìïëÿð êëàññà Finder ÿâëÿåòñÿ Iterator (èòåðàòîðîì) PHP. Òàê, ÷òî âìåñòî
    ïðîõîäà íàä Finder'îì ñ ïîìîùüþ foreach, ìîæíî òàêæå êîíâåðòèðîâàòü åãî â ìàññèâ ñ ïîìîùüþ ìåòîäà :phpfunction:`iterator_to_array`, èëè ïîëó÷èòü êîëè÷åñòâî
    ýëåìåíòîâ, ñ ïîìîùüþ :phpfunction:`iterator_count`.

    3.15.2 Êðèòåðèè ïîèñêà
    Ðàñïîëîæåíèå

    Ðàñïîëîæåíèå ÿâëÿåòñÿ åäèíñòâåííûì îáÿçàòåëüíûì ïàðàìåòðîì. Äàííûé ïàðàìåòð
    óêàçûâàåò ïîèñêîâèêó êàêóþ äèðåêòîðèþ èñïîëüçîâàòü äëÿ ïîèñêà:
    $finder->in(__DIR__);

    Ïîèñê â íåñêîëüêèõ ìåñòàõ ðåàëèçóåòñÿ ñ ïîìîùüþ ïîñëåäîâàòåëüíûõ âûçîâîâ ìåòîäà
    :method:`Symfony\\Component\\Finder\\Finder::in`:
    3.15.

    Êàê èñêàòü ôàéëû

    359

    Symfony Documentation, Âûïóñê 2.0

    $finder->files()->in(__DIR__)->in('/elsewhere');

    Èñêëþ÷åíèå

    êàòàëîãîâ

    èç

    ïîèñêà

    îñóùåñòâëÿåòñÿ

    :method:`Symfony\\Component\\Finder\\Finder::exclude`

    ìåòîäîì

    $finder->in(__DIR__)->exclude('ruby');

    Ò.ê. Finder èñïîëüçóåò PHP èòåðàòîðû, åìó ìîæíî ïåðåäàòü ëþáîé URL ñ ïîääåðæèâàåìûì ïðîòîêîëîì protocol:
    $finder->in('ftp://example.com/pub/');

    Òàêæå îí ðàáîòàåò ñ ïîëüçîâàòåëüñêèìè ïîòîêàìè:
    use Symfony\Component\Finder\Finder;
    $s3 = new \Zend_Service_Amazon_S3($key, $secret);
    $s3->registerStreamWrapper("s3");
    $finder = new Finder();
    $finder->name('photos*')->size('< 100K')->date('since 1 hour ago');
    foreach ($finder->in('s3://bucket-name') as $file) {
    // do something
    }

    print $file->getFilename()."\n";

    Ïðèìå÷àíèå:  äîêóìåíòàöèè Streams ìîæíî óçíàòü êàê ñîçäàâàòü ñâîè ñîáñòâåííûå
    ïîòîêè.

    Ôàéëû èëè êàòàëîãè

    Ïî-óìîë÷àíèþ,
    Finder
    âîçâðàùàåò
    ôàéëû
    èëè
    êàòàëîãè;
    íî
    ìåòîäàìè
    :method:`Symfony\\Component\\Finder\\Finder::les`
    è
    :method:`Symfony\\Component\\Finder\\Finder::directories` ìîæíî óïðàâëÿòü åãî ïîâåäåíèåì:
    $finder->files();
    $finder->directories();

    Åñëè õîòèòå ñëåäîâàòü ïî ññûëêàì, èñïîëüçóéòå ìåòîä followLinks():
    $finder->files()->followLinks();

    360

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    Ïî-óìîë÷àíèþ, èòåðàòîð èãíîðèðóåò ïîïóëÿðíûå ôàéëû VCS. Äàííîå ïîâåäåíèå ìîæåò
    áûòü èçìåíåíî ñ ïîìîùüþ ìåòîäà ignoreVCS():
    $finder->ignoreVCS(false);

    Ñîðòèðîâêà

    Ñîðòèðîâêà ðåçóëüòàòîâ ïî èìåíè èëè òèïó (êàòàëîãè ïåðâûìè, ôàéëû ïîñëåäíèìè):
    $finder->sortByName();
    $finder->sortByType();

    Ïðèìå÷àíèå: Îáðàòèòå âíèìàíèå, ÷òî ìåòîäàì sort* òðåáóåòñÿ ïîëó÷èòü âñå ïîäõîäÿ-

    ùèå ïîä îáðàáîòêó îáúåêòû. Äàííàÿ ïðîöåäóðà ïðè áîëüøèõ îáúåìàõ âåñüìà ìåäëåííà.

    Òàêæå ìîæíî îïðåäåëèòü ñâîè ñîáñòâåííûå àëãîðèòìû ñîðòèðîâêè ñ ïîìîùüþ ìåòîäà
    sort():
    $sort = function (\SplFileInfo $a, \SplFileInfo $b)
    {
    return strcmp($a->getRealpath(), $b->getRealpath());
    };
    $finder->sort($sort);

    Èìåíà ôàéëîâ

    Íàëîæèòü

    îãðàíè÷åíèÿ

    ïî

    èìåíè

    ôàéëîâ

    ìîæíî

    :method:`Symfony\\Component\\Finder\\Finder::name`:

    ñ

    ïîìîùüþ

    ìåòîäà

    $finder->files()->name('*.php');

    Ìåòîä name() ïðèíèìàåò ñòðîêè, ðåãóëÿðíûå âûðàæåíèÿ èëè øàáëîíû:
    $finder->files()->name('/\.php$/');

    Ìåòîä notNames() èñêëþ÷àåò ôàéëû ïî øàáëîíó:
    $finder->files()->notName('*.rb');

    3.15.

    Êàê èñêàòü ôàéëû

    361

    Symfony Documentation, Âûïóñê 2.0

    Ðàçìåð ôàéëà

    Îãðàíè÷èòü ðàçìåð ôàéëîâ ìîæíî ñ ïîìîùüþ ìåòîäà Restrict les by size with the
    :method:`Symfony\\Component\\Finder\\Finder::size`:
    :method:`Symfony\\Component\\Finder\\Finder::size` method::

    $nder->les()->size(`< 1.5K');
    Îãðàíè÷èòü ðàçìåð â ðàìêàõ ìîæíî ñ ïîìîùüþ ñâÿçàííûõ âûçîâîâ:
    Îïåðàòîð ñðàâíåíèÿ ìîæåò áûòü ëþáûì èç ñëåäóþùèõ: >, >=, <, `<=', `=='.
    Öåëåâîå çíà÷åíèå ìîæåò èñïîëüçîâàòü ïðèñòàâêè (k, ki) êèëîáàéòû, (m, mi) ìåãàáàéòû, èëè (g, gi) ãèãàáàéòû. Òå êîòîðûå èñïîëüçóþò ñóôôèêñû i (êèáè/ìèáè è ò.ä.) â
    íàçâàíèè ÿâëÿþòñÿ âåðñèÿìè 2**n ñîãëàñíî ñòàíäàðòó`IEC standard`_.
    Äàòà ôàéëà

    Ñ ïîìîùüþ ìåòîäà :method:`Symfony\\Component\\Finder\\Finder::date` ìîæíî íàëîæèòü îãðàíè÷åíèÿ íà ôàéëû ïî äàòå ïîñëåäíåãî èçìåíåíèÿ:
    $finder->date('since yesterday');

    Îïåðàòîð ñðàâíåíèÿ ìîæåò áûòü ëþáûì èç ñëåäóþùèõ:>, >=, <, `<=', `=='. Òàêæå ìîæíî èñïîëüçîâàòü ïñåâäîíèìû since èëè after äëÿ îïåðàòîðà >, è until èëè before â
    êà÷åñòâå <.
    Öåëåâîå çíà÷åíèå ìîæåò áûòü ëþáîé äàòîé ïîääåðæèâàåìîé ôóíêöèåé strtotime
    Ãëóáèíà êàòàëîãîâ

    Ïî-óìîë÷àíèþ
    íî.
    Îãðàíè÷èòü

    Finder
    ãëóáèíó

    ïðîñìàòðèâàåò
    ïîèñêà
    ìîæíî

    êàòàëîãè
    ðåêóðñèâñ
    ïîìîùüþ
    ìåòîäà
    :method:`Symfony\\Component\\Finder\\Finder::depth`:
    $finder->depth('== 0');
    $finder->depth('< 3');

    Ôèëüòðàöèÿ

    Îãðàíè÷èòü ðåçóëüòàòû ïîèñêà ñîãëàñíî ñîáñòâåííûì ïàðàìåòðàì, ìîæíî èñïîëüçóÿ
    ìåòîä :method:`Symfony\\Component\\Finder\\Finder::lter`:

    362

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

    $filter = function (\SplFileInfo $file)
    {
    if (strlen($file) > 10) {
    return false;
    }
    };
    $finder->files()->filter($filter);

    Ìåòîä filter() ïîëó÷àåò çàìûêàíèå â êà÷åñòâå àðãóìåíòà. Äëÿ êàæäîãî ïîäõîäÿùåãî
    ôàéëà, çàìûêàíèå âûçûâàåòñÿ ñ àðãóìåíòîì â âèäå îáúåêòà êîòîðûé ÿâëÿåòñÿ ýêçåìïëÿðîì êëàññà :phpclass:`SplFileInfo`. Ôàéë èñêëþ÷àåòñÿ èç ìíîæåñòâà ðåçóëüòàòîâ
    åñëè çàìûêàíèå âîçâðàùàåò false.
    ˆ Ïðîöåññ

    

    Êàê ñîçäàòü è ðàçìåñòèòü Ïðîåêò íà Symfony2 â git-ðåïîçèòîðèè

    ˆ Êîíòðîëëåðû

    

    Êàê ñîçäàòü ñîáñòâåííûå ñòðàíèöû îøèáîê

    

    Êàê îïðåäåëÿòü Êîíòðîëëåðû â êà÷åñòâå ñåðâèñîâ

    ˆ Ìàðøðóòèçàöèÿ

    

    Êàê çàñòàâèòü ìàðøðóòèçàòîð âñåãäà èñïîëüçîâàòü HTTPS èëè HTTP

     /cookbook/routing/slash_in_parameter
    ˆ Ðàáîòà ñ JavaScript è CSS ðåñóðñàìè

     /cookbook/assetic/asset_management
     /cookbook/assetic/yuicompressor
     /cookbook/assetic/jpeg_optimize
     /cookbook/assetic/apply_to_option
    ˆ Âçàèìîäåñòâèå ñ ÑÓÁÄ (Doctrine)

     /cookbook/doctrine/file_uploads
     /cookbook/doctrine/common_extensions
     /cookbook/doctrine/event_listeners_subscribers
     /cookbook/doctrine/dbal
     /cookbook/doctrine/reverse_engineering
     /cookbook/doctrine/multiple_entity_managers
     /cookbook/doctrine/custom_dql_functions
    3.15.

    Êàê èñêàòü ôàéëû

    363

    Symfony Documentation, Âûïóñê 2.0

    ˆ Ôîðìû è Âàëèäàöèÿ

     /cookbook/form/form_customization
     /cookbook/form/create_custom_field_type
     /cookbook/validation/custom_constraint
     (doctrine) /cookbook/doctrine/file_uploads
    ˆ Êîíôèãóðàöèÿ è Ñåðâèñ Êîíòåéíåð

     /cookbook/configuration/environments
     /cookbook/configuration/external_parameters
     /cookbook/service_container/factories
     /cookbook/service_container/parentservices
     /cookbook/service_container/scopes
     /cookbook/configuration/pdo_session_storage
    ˆ Ïàêåòû

     /cookbook/bundles/best_practices
     /cookbook/bundles/inheritance
     /cookbook/bundles/override
     /cookbook/bundles/extension
    ˆ Ðàáîòà ñ Email

    

    Êàê îòïðàâëÿòü ýëåêòðîííóþ ïî÷òó

    

    Êàê èñïîëüçîâàòü Gmail äëÿ îòïðàâêè ýëåêòðîííûõ ïèñåì

     /cookbook/email/dev_environment
     /cookbook/email/spool
    ˆ Òåñòèðîâàíèå

    

    Êàê ñìîäåëèðîâàòü HTTP àóòåíòèôèêàöèþ â Ôóíêöèîíàëüíîì òåñòå

    

    Êàê òåñòèðîâàòü âçàèìîäåéñòâèå ñ íåñêîëüêèìè êëèåíòàìè

    

    Êàê èñïîëüçîâàòü ïðîôèëèðîâùèê â Ôóíêöèîíàëüíîì òåñòå

     /cookbook/testing/doctrine
    ˆ Áåçîïàñíîñòü

     /cookbook/security/remember_me
     /cookbook/security/voters
    364

    Ãëàâà 3.

    Cookbook

    Symfony Documentation, Âûïóñê 2.0

     /cookbook/security/acl
     /cookbook/security/acl_advanced
     /cookbook/security/force_https
     /cookbook/security/form_login
     /cookbook/security/securing_services
     /cookbook/security/entity_provider
     /cookbook/security/custom_provider
     /cookbook/security/custom_authentication_provider
     /cookbook/security/target_path
    ˆ Êýøèðîâàíèå

    

    Êàê èñïîëüçîâàòü Varnish äëÿ óñêîðåíèÿ ðàáîòû ñàéòà

    ˆ Øàáëîíû

    

    Âíåäðåíèå ïåðåìåííûõ âî âñå øàáëîíû (ò.í. Ãëîáàëüíûå ïåðåìåííûå)

    

    Êàê èñïîëüçîâàòü PHP øàáëîíû âìåñðî Twig

    ˆ Èíñòðóìåíòû, Ëîããèðîâàíèå âíóòðåííèå êîìïîíåíòû

    

    Êàê àâòîìàòè÷åñêè çàãðóæàòü êëàññû

    

    Êàê èñêàòü ôàéëû

     /cookbook/console
     /cookbook/debugging
    

    Êàê èñïîëüçîâàòü Monolog äëÿ æóðíàëèðîâàíèÿ

    ˆ Web Ñåðâèñû

     /cookbook/web_services/php_soap_extension
    ˆ Ðàñøèðåíèå Symfony

     /cookbook/event_dispatcher/class_extension
     /cookbook/event_dispatcher/method_behavior
     /cookbook/request/mime_type
     /cookbook/profiler/data_collector
    ˆ Symfony2 äëÿ ðàçðàáîò÷èêîâ symfony1

     /cookbook/symfony1
    Ïðî÷èòàéòå
    3.15.

    Ðåöåïòû .

    Êàê èñêàòü ôàéëû

    365

    Symfony Documentation, Âûïóñê 2.0

    366

    Ãëàâà 3.

    Cookbook

    ×àñòü IV
    Ñïðàâî÷íûå äîêóìåíòû

    367

    Symfony Documentation, Âûïóñê 2.0

    Ñ íèìè âû áûñòðî ïîëó÷èòå îòâåòû:

    369

    Symfony Documentation, Âûïóñê 2.0

    370

    Ãëàâà

    4

    Reference Documents
    4.1 Ñïðàâî÷íèê òèïîâ ïîëåé äëÿ ôîðì
    4.1.1 Òèï ïîëÿ collection

    Ñì. êëàññ Symfony\Component\Form\Extension\Core\Type\CollectionType.
    4.1.2 Ïîëå date

    Ýòî ïîëå ïîçâîëÿåò ïîëüçîâàòåëþ èçìåíÿòü èíôîðìàöèþ î äàòå ïðè ïîìîùè ðàçëè÷íûõ
    HTML-ýëåìåíòîâ.
    Ñîîòâåòñòâóþùèå äàííûå äëÿ ýòîãî ïîëÿ äîëæíû áûòü â âèäå îáúåêòà \DateTime, ñòðîêè, òàéìøòàìïà èëè ìàññèâà. Åñëè îïöèÿ `input`_ óêàçàíà âåðíî, ïîëå áóäåò çàáîòèòüñÿ î äåòàëÿõ ñàìîñòîÿòåëüíî.
    Ýòî ïîëå ìîæåò áûòü îòîáðàæåíî êàê îäèí òåêñòáîêñ, òðè òåêñòáîêñà (ìåñÿö, äåíü, ãîä)
    èëè æå òðè ñåëåêòáîêñà (ñì. îïöèþ widget_).

    371

    Symfony Documentation, Âûïóñê 2.0

    Òèï äàííûõ
    Îòîáðàæàåòñÿ êàê
    Îïöèè

    Ðîäèòåëüñêèé òèï
    Êëàññ

    \DateTime, string, timestamp, èëè array
    (ñì. îïöèþ input)
    îäèí text box èëè òðè ïîëÿ select
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ
    ˆ

    `widget`_
    `input`_

    empty_value

    `years`_
    `months`_
    `days`_
    `format`_
    `pattern`_
    `data_timezone`_
    `user_timezone`_

    field (åñëè òåêñò), èíà÷å form
    Symfony\Component\Form\Extension\Core\Type\DateTy

    Ïðèìåðû èñïîëüçîâàíèÿ

    Ýòî ïîëå èìååò ìíîãî íàòðîåêò, íî îíî ïðîñòî â èñïîëüçîâàíèè. Íàèáîëåå âàæíûìè
    îïöèÿìè ÿâëÿþòñÿ input è widget.
    Ïîëàãàÿ, ÷òî ó âàñ åñòü ïîëå publishedAt, êîòîðîå ñîäåðæèò äàòó â âèäå îáõåêòà
    DateTime. Ñëåäóþùèé êîä êîíôèãóðèðóåò ïîëå date äëÿ ýòîãî ïîëÿ â âèòå òð¼õ ñåëåêòîâ:
    $builder->add('publishedAt', 'date', array(
    'input' => 'datetime',
    'widget' => 'choice',
    ));

    Îïöèÿ input äîëæíà âñåãäà ñîîòâåòñòâîâàòü òèïó äàííûõ, êîòîðûå èñïîëüçóþòñÿ äëÿ
    ýòîãî ïîëÿ. Íàïðèìåð, åñëè ïîëå publishedAt èñïîëüçóåò unix timestamp, âàì íóæíî
    ïðèñâîèòü îïöèè input çíà÷åíèå timestamp:
    $builder->add('publishedAt', 'date', array(
    'input' => 'timestamp',
    'widget' => 'choice',
    ));

    Ýòî ïîëå òàêæå ïîäåðæèâàåò array è string â êà÷åñòâå âàëèäíûõ îïöèé äëÿ input.

    372

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    Îïöèè ïîëÿ

    empty_value

    type: string | array
    Åñëè îïöèÿ widget èìååò çíà÷åíèå choice, òîãäà ýòî ïîëå áóäåò ïðåäñòàâëåíî ïîñëåäîâàòåëüíîñòüþ ñåëåêòáîêñîâ. Îïöèÿ empty_value ìîæåò áûòü èñïîëüçîâàíà äëÿ äîáàâëåíèÿ ïóñòîé îïöèè â íà÷àëå ñïèñêà ñåëåêòáîêñà:
    $builder->add('dueDate', 'date', array(
    'empty_value' => '',
    ));

    Òàêæå âû ìîæåòå óêàçàòü ñòðîêó, êîòîðàÿ áóäåò çíà÷åíèåì ïóñòîé îïöèè â selectbox:
    $builder->add('dueDate', 'date', array(
    'empty_value' => array('year' => 'Year', 'month' => 'Month', 'day' => 'Day')
    ));

    Ôîðìà ñîñòîèò èç ïîëåé, êàæäîå èç êîòîðûõ ñîçäà¼òñÿ ïðè ïîìîùè òèïîâ ïîëåé (íàïðèìåð, òèï text, òèï choice è ò.ä.). Symfony2 ïîñòàâëÿåòñÿ ñ áîëüøèì êîëè÷åñòâîì
    òèïîâ ïîëåé, êîòîðûå ìîãóò áûòü èñïîëüçîâàíû â âàøåì ïðèëîæåíèè.
    4.1.3 Ïîääåðæèâàåìûå òèïû ïîëåé

    Ñëåäóþùèå òèïû ïîëåé äîñòóïíû â Symfony2:
    Òåêñòîâûå ïîëÿ

    ˆ text
    ˆ textarea
    ˆ email
    ˆ integer
    ˆ money
    ˆ number
    ˆ password
    ˆ percent
    ˆ search
    4.1.

    Ñïðàâî÷íèê òèïîâ ïîëåé äëÿ ôîðì

    373

    Symfony Documentation, Âûïóñê 2.0

    ˆ url
    Ïîëÿ äëÿ âûáîðà

    ˆ choice
    ˆ entity
    ˆ country
    ˆ language
    ˆ locale
    ˆ timezone
    Ïîëÿ äëÿ äàòû è âðåìåíè

    ˆ

    date

    ˆ datetime
    ˆ time
    ˆ birthday
    Ïðî÷èå ïîëÿ

    ˆ checkbox
    ˆ file
    ˆ radio
    Ãðóïïû ïîëåé

    ˆ

    collection

    ˆ repeated
    Ñêðûòûå ïîëÿ

    ˆ hidden
    ˆ csrf

    374

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    Áàçîâûå ïîëÿ

    ˆ field
    ˆ form

    4.2 Ñïðàâî÷íèê ôóíêöèé Twig äëÿ ðàáîòû ñ ôîðìàìè
    Ýòî ñïðàâî÷íîå ðóêîâîäñòâî ñîäåðæèò âñå ôóíêöèè Twig, äîñòóïíûå ïðè îòîáðàæåíèè
    ôîðì. Èìååòñÿ íåñêîëüêî òàêèõ ôóíêöèé, êàæäàÿ èç êîòîðûõ îòâå÷àåò çà îòîáðàæåíèå
    ñâîåé ÷àñòè ôîðìû (ìåòêè, îøèáêè, âèäæåòû è ò.ä.).
    4.2.1 form_label(form.name, label, variables)

    Îòîáðàæàåò ìåòêó äëÿ äàííîãî ïîëÿ. Îïöèîíàëüíî âû ìîæåòå ïåðåäàòü ñâîé òåêñò äëÿ
    ìåòêè â êà÷åñòâå âòîðîãî àðãóìåíòà.
    {{ form_label(form.name) }}
    {# Ýòè äâå ôîðìû çàïèñè ýêâèâàëåíòíû #}
    {{ form_label(form.name, 'Your Name', { 'attr': {'class': 'foo'} }) }}
    {{ form_label(form.name, null, { 'label': 'Your name', 'attr': {'class': 'foo'} }) }}

    4.2.2 form_errors(form.name)

    Îòîáðàæàåò îøèáêè äëÿ äàííîãî ïîëÿ.
    {{ form_errors(form.name) }}
    {# îòîáðàæàåò "ãëîáàëüíûå" îøèáêè ôîðìû #}
    {{ form_errors(form) }}

    4.2.3 form_widget(form.name, variables)

    Îòîáðàæàåò HTML âèäæåò äëÿ äàííîãî ïîëÿ. Åñëè âû èñïîëüçóåòå ýòîò õåëïåð äëÿ
    âñåé ôîðìû èëè íàáîðà ïîëåé, îíè áóäóò ïîëíîñòüþ îòîáðàæåíû.
    {# îòîáðàæàåò âèäæåò è äîáàâëÿåò åìó êëàññ "foo" #}
    {{ form_widget(form.name, { 'attr': {'class': 'foo'} }) }}

    4.2.

    Ñïðàâî÷íèê ôóíêöèé Twig äëÿ ðàáîòû ñ ôîðìàìè

    375

    Symfony Documentation, Âûïóñê 2.0

    Âòîðîé àðãóìåíò form_widget - ýòî ìàññèâ ïåðåìåííûõ. Íàèáîëåå òèïè÷íîé èç íèõ
    ÿâëÿåòñÿ attr, êîòîðûé ïðåäñòàâëÿåò ñîáîé ìññèâ HTML-àòðèáóòîâ, êîòîðûå áóäóò
    ïðèìåíåíû ê HTML-âèäæåòó.  íåêîòîðûõ ñëó÷àÿõ, ðÿä òèïîâ ïîëåé òàêæå èìåþò
    äðóãèå îïöèè, îòíîñÿùèåñÿ ê îòîáðàæåíèþ øàáëîíà. Ýòè îïöèè îïèñàíû äëÿ êàæäîãî
    èç òàêèõ òèïîâ ïîëåé.
    4.2.4 form_row(form.name, variables)

    Îòîáðàæàåò ñòðîêó äëÿ çàäàííîãî ïîëÿ, êîòîðàÿ ÿâëÿåòñÿ êîìáèíàöèåé ìåòêè, îøèáîê è âèäæåòà.
    {# îòîáðàæàåì ñòðîêó, ìåòêó çàìåíÿåì íà "foo" #}
    {{ form_row(form.name, { 'label': 'foo' }) }}

    Âòîðîé àðãóìåíò form_row - ýòî ìàññèâ. Øàáëîíû, ïðåäîñòàâëÿåìûå â Symfony ïîçâîëÿþò èçìåíÿòü ëèøü ìåòêó, êàê, è ïîêàçàíî â ïðèìåðå âûøå.
    4.2.5 form_rest(form, variables)

    Îòîáðàæàåò âñå ïîëÿ ôîðìû, êîòîðûå åù¼ íå áûëè îòîáðàæåíû. Õîðîøèì ïîäõîäîì
    ÿâëÿåòñÿ âñåãäà èñïîëüçîâàòü ýòîò õåëïåð â âàøèõ ôîðìàõ äëÿ îòîáðàæåíèÿ ñêðûòûõ
    ïîëåé, à òàêæå ïîëåé, êîòîðûå âû ìîãëè ñëó÷àéíî çàáûòü.
    {{ form_rest(form) }}

    4.2.6 form_enctype(form)

    Åñëè ïîëå ñîäåðæèò õîòü îäíî ïîëå äëÿ çàãðóçêè ôàéëà, ýòîò õåëïåð îòîáðàçèò àòðèáóò
    ôîðìû enctype="multipart/form-data". Õîðîøåé ïðàêòèêîé ÿâëÿåòñÿ åãî èñïîëüçîâàíèå âî âñåõ ôîðìàõ:


    4.3 Òàãè Dependency Injection
    Ñïèñîê äîñòóïíûõ òàãîâ:
    ˆ data_collector
    ˆ form.type
    ˆ form.type_extension

    376

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    ˆ form.type_guesser
    ˆ kernel.cache_warmer
    ˆ kernel.event_listener
    ˆ kernel.event_subscriber
    ˆ monolog.logger
    ˆ monolog.processor
    ˆ templating.helper
    ˆ routing.loader
    ˆ translation.loader
    ˆ twig.extension
    ˆ validator.initializer
    4.3.1 Ïîäêëþ÷àåì ïîëüçîâàòåëüñêèå õåëïåðû â PHP øàáëîíû

    Äëÿ ïîäêëþ÷åíèÿ ïîëüçîâàòåëüñêîãî õåëïåðà, äîáàâüòå åãî â âèäå ñëóæáû â îäèí èç
    âàøèõ êîíôèãóðàöèîííûõ ôàéëîâ, ïîìåòüòå åãî òàãîì templating.helper è îïðåäåëèòå
    àòðèáóò alias (â øàáëîíàõ õåëïåð áóäåò äîñòóïåí ÷åðåç ýòîò àëèàñ):
    ˆ

    YAML

    services:
    templating.helper.your_helper_name:
    class: Fully\Qualified\Helper\Class\Name
    tags:
    - { name: templating.helper, alias: alias_name }

    ˆ

    XML





    ˆ

    PHP

    $container
    ->register('templating.helper.your_helper_name', 'Fully\Qualified\Helper\Class\Name')
    ->addTag('templating.helper', array('alias' => 'alias_name'))
    ;

    4.3.

    Òàãè Dependency Injection

    377

    Symfony Documentation, Âûïóñê 2.0

    4.3.2 Ïîäêëþ÷åíèå ïîëüçîâàòåëüñêèõ ðàñøèðåíèé äëÿ Twig

    Äëÿ àêòèâàöèè ðàñøèðåíèÿ Twig äîáàâüòå åãî â âèäå ñëóæáû â îäèí èç âàøèõ êîíôèãóðàöèîííûõ ôàéëîâ è ïîìåòüòå ýòó ñëóæáó òàãîì twig.extension:
    ˆ

    YAML

    services:
    twig.extension.your_extension_name:
    class: Fully\Qualified\Extension\Class\Name
    tags:
    - { name: twig.extension }

    ˆ

    XML




    ˆ

    PHP

    $container
    ->register('twig.extension.your_extension_name', 'Fully\Qualified\Extension\Class\Name'
    ->addTag('twig.extension')
    ;

    Î òîì, êàê ñîçäàâàòü ðàñøèðåíèÿ äëÿ Twig, ÷èòàéòå â äîêóìåíòàöèè Twig.
    4.3.3 Ïîäêëþ÷åíèå ïîëüçîâàòåëüñêèõ ñëóøàòåëåé

    Äëÿ òîãî ÷òîáû ïîäêëþ÷èòü ñëóøàòåëÿ (listener), äîáàâüòå åãî â âèäå ñëóæáû â îäèí
    èç âàøèõ êîíôèãóðàöèîííûõ ôàéëîâ è ïîìåòüòå å¼ òàãîì kernel.event_listener. Âû
    ìîæåòå óêàçàòü íàèìåíîâàíèå ñîáûòèÿ, êîòîðîå áóäåò ñëóøàòü âàøà ñëóæáà, à òàêæå
    ìåòîä, êîòîðûé áóäåò âûçâàí:
    ˆ

    YAML

    services:
    kernel.listener.your_listener_name:
    class: Fully\Qualified\Listener\Class\Name
    tags:
    - { name: kernel.event_listener, event: xxx, method: onXxx }

    ˆ

    XML



    378

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    PHP

    $container
    ->register('kernel.listener.your_listener_name', 'Fully\Qualified\Listener\Class\Name')
    ->addTag('kernel.event_listener', array('event' => 'xxx', 'method' => 'onXxx'))
    ;

    Ïðèìå÷àíèå: Âû òàêæå ìîæåòå óêàçàòü ïðèîðèòåò (ïîëîæèòåëüíîå èëè îòðèöàòåëüíîå öåëîå ÷èñëî) â âèäå àòðèáóòà òàãà kernel.event_listener (òàêæå êàê ìåòîä èëè ñîáûòèå). Ýòî ïîçâîëÿåò âàì óïðàâëÿòü î÷åð¼äíîñòüþ âûçîâà ñëóøàòåëåé îäíîãî è òîãî æå
    ñîáûòèÿ.

    4.3.4 Ïîäêëþ÷åíèå ïîëüçîâàòåëüñêèõ Ïîäïèñ÷èêîâ

    Äîáàâëåíî â âåðñèè 2.1. Äëÿ àêòèâàöèè ïîäïèñ÷èêà, äîáàâüòå åãî â âèäå ñëóæáû â îäèí
    èç êîíôèãóðàöèîííûõ ôàéëîâ è ïîìåòüòå åãî òàãîì kernel.event_subscriber:
    ˆ

    YAML

    services:
    kernel.subscriber.your_subscriber_name:
    class: Fully\Qualified\Subscriber\Class\Name
    tags:
    - { name: kernel.event_subscriber }

    ˆ

    XML




    ˆ

    PHP

    $container
    ->register('kernel.subscriber.your_subscriber_name', 'Fully\Qualified\Subscriber\Class\
    ->addTag('kernel.event_subscriber')
    ;

    Ïðèìå÷àíèå:

    ðåàëèçîâûâàòü
    SymfonyComponentEventDispatcherEventSubscriberInterface.

    4.3.

    Ñëóæáà

    Òàãè Dependency Injection

    äîëæíà

    èíòåðôåéñ

    379

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìå÷àíèå: Åñëè âàøà ñëóæáà ñîçäà¼òñÿ ôàáðèêîé, âû ÄÎËÆÍÛ êîððåêòíî
    óêàçàòü ïàðàìåòð class äëÿ ýòîãî òàãà, èíà÷å ðàáîòàòü íå áóäåò.

    4.3.5 Ïîäêëþ÷åíèå ïîëüçîâàòåëüñêèõ øàáëîíèçàòîðîâ

    Äëÿ òîãî ÷òîáû àêòèâèðîâàòü åù¼ îäèí øàáëîíèçàòîð, äîáàâüòå åãî â âèäå ñëóæáû â
    îäèí èç êîíôèãóðàöèîííûõ ôàéëîâ è ïîìåòüòå å¼ òàãîì templating.engine:
    ˆ

    YAML

    services:
    templating.engine.your_engine_name:
    class: Fully\Qualified\Engine\Class\Name
    tags:
    - { name: templating.engine }

    ˆ

    XML





    ˆ

    PHP

    $container
    ->register('templating.engine.your_engine_name', 'Fully\Qualified\Engine\Class\Name')
    ->addTag('templating.engine')
    ;

    4.3.6 Ïîäêëþ÷åíèå ïîëüçîâàòåëüñêèõ çàãðóç÷èêîâ äëÿ Ìàðøðóòîâ

    Äëÿ òîãî ÷òîáû ïîäêëþ÷èòü çàãðóç÷èê ìàðøðóòîâ, äîáàâüòå åãî â âèäå ñëóæáû â îäèí
    èç êîíôèãóðàöèîííûõ ôàéëîâ è ïîìåòüòå å¼ òàãîì routing.loader:
    ˆ

    YAML

    services:
    routing.loader.your_loader_name:
    class: Fully\Qualified\Loader\Class\Name
    tags:
    - { name: routing.loader }

    ˆ

    380

    XML

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0





    4.3.7 Èñïîëüçîâàíèå

    îòäåëüíîãî

    êàíàëà

    äëÿ

    ëîããèðîâàíèÿ

    â

    Monolog

    Ìîíîëîã ïîçâîëÿåò âàì äåëèòü îáðàáîò÷èêè ìåæäó íåñêîëüêèìè êàíàëàìè ëîããèðîâàíèÿ. Ñëóæáà ëîããåðà èñïîëüçóåò êàíàë app, íî âû ìîæåòå èçìåíÿòü êàíàë ïðè èíúåêöèè
    ëîããåðà â ñëóæáó.
    ˆ

    YAML

    services:
    my_service:
    class: Fully\Qualified\Loader\Class\Name
    arguments: [@logger]
    tags:
    - { name: monolog.logger, channel: acme }

    ˆ

    XML






    ˆ

    PHP

    $definition = new Definition('Fully\Qualified\Loader\Class\Name', array(new Reference('logg
    $definition->addTag('monolog.logger', array('channel' => 'acme'));
    $container->register('my_service', $definition);;

    Ïðèìå÷àíèå: Ýòî ðàáîòàåò, òîëüêî êîãäà ñëóæáà ëîããåðà èíæåêòèòñÿ ÷åðåç àðãóìåíò

    êîíñòðóêòîðà, ïðè èñïîëüçîâàíèè ñåòòåðà ðàáîòàòü íå áóäåò.

    4.3.8 Äîáàâëåíèå ïðîöåññîðîâ äëÿ Monolog

    Monolog ïîçâîëÿåò âàì äîáàâëÿòü ïðîöåññîðû â ëîããåð èëè â õýíäëåðû äëÿ äîáàâëåíèÿ
    äîïîëíèòåëüíûõ äàííûõ â çàïèñè. Ïðîöåññîð ïîëó÷àåò çàïèñü â êà÷åñòâå àðãóìåíòà è
    äîëæåí âåðíóòü å¼ ïîñëå äîáàâëåíèÿ äîïîëíèòåëüíûõ äàííûõ â àòðèáóò çàïèñè extra.

    4.3.

    Òàãè Dependency Injection

    381

    Symfony Documentation, Âûïóñê 2.0

    Äàâàéòå ïîñìîòðèì, êàê âû ìîæåòå èñïîëüçîâàòü âñòðîåííûé IntrospectionProcessor
    äëÿ äîáàâëåíèÿ ôàéëà, ñòðîêè, êëàññà è ìåòîäà, ãäå áûë âûçâàí ëîããåð.
    Âû ìîæåòå äîáàâèòü ïðîöåññîð ãëîáàëüíî:
    ˆ

    YAML

    services:
    my_service:
    class: Monolog\Processor\IntrospectionProcessor
    tags:
    - { name: monolog.processor }

    ˆ

    XML





    ˆ

    PHP

    $definition = new Definition('Monolog\Processor\IntrospectionProcessor');
    $definition->addTag('monolog.processor');
    $container->register('my_service', $definition);

    Ñîâåò: Åñëè ñëóæáà íå ìîæåò áûòü âûçâàíà (ïðè ïîìîùè __invoke) âû ìîæåòå äîáàâèòü àòðèáóò method â òàã, ÷òîáû èñïîëüçîâàòü óêàçàííûé ìåòîä.

    Âû òàêæå ìîæåòå äîáàâèòü ïðîöåññîð äëÿ íåêîòîðîãî õýíäëåðà ïðè ïîìîùè àòðèáóòà
    handler:
    ˆ

    YAML

    services:
    my_service:
    class: Monolog\Processor\IntrospectionProcessor
    tags:
    - { name: monolog.processor, handler: firephp }

    ˆ

    XML





    ˆ

    382

    PHP

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    $definition = new Definition('Monolog\Processor\IntrospectionProcessor');
    $definition->addTag('monolog.processor', array('handler' => 'firephp');
    $container->register('my_service', $definition);

    Âû òàêæå ìîæåòå äîáàâèòü ïðîöåññîð äëÿ íåêîòîðîãî êàíàëà ëîããèíãà ïðè ïîìîùè
    àòðèáóòà channel. Ýòîò àòðèáóò çàðåãèñòðèðóåò ïðîöåññîð òîëüêî äëÿ êàíàëà security,
    èñïîëüçóåìîãî â êîìïîíåíòå Security:
    ˆ

    YAML

    services:
    my_service:
    class: Monolog\Processor\IntrospectionProcessor
    tags:
    - { name: monolog.processor, channel: security }

    ˆ

    XML





    ˆ

    PHP

    $definition = new Definition('Monolog\Processor\IntrospectionProcessor');
    $definition->addTag('monolog.processor', array('channel' => 'security');
    $container->register('my_service', $definition);

    Ïðèìå÷àíèå: Âû íå ìîæåòå èñïîëüçîâàòü îáà àòðèáóòà handler è channel äëÿ îäíîãî
    è òîãî æå òàãà òàê êàê õýíäëåðû äîñòóïíû äëÿ âñåõ êàíàëîâ.
    ˆ Êîíôèãóðèðîâàíèå:
    Åñëè âû õîòü ðàç çàäàâàëèñü âîïðîñîì, ÷òî îçíà÷àþò ðàçëè÷íûå îïöèè â âàøèõ
    êîíôèãóðàöèîííûõ ôàéëàõ, òàêèõ êàê app/config/config.yml, òî ýòîò ðàçäåë
    äëÿ âàñ. Çäåñü âñå êîíôèãóðàöèîííûå ïàðàìåòðû ðàçáèòû íà ïîäðàçäåëû ïî êëþ÷ó (íàïðèìåð framework), êîòîðûå îïðåäåëÿþò âñå âîçìîæíûå ñåêöèè â íàñòðîéêàõ Symfony2.

     framework
     doctrine
     security
     assetic

    4.3.

    Òàãè Dependency Injection

    383

    Symfony Documentation, Âûïóñê 2.0

     swiftmailer
     twig
     monolog
     web_profiler
    ˆ Ôîðìû è âàëèäàöèÿ

    

    Ôîðìû, ñïðàâî÷íèê òèïîâ ïîëåé

     Âàëèäàöèÿ, ñïðàâî÷íèê îãðàíè÷åíèé
    

    Twig: ñïðàâî÷íèê ôóíêöèé äëÿ ðàáîòû ñ ôîðìàìè

    ˆ Îñòàëüíûå ðàçäåëû

    

    Òàãè Dependency Injection

     /reference/YAML
     /reference/requirements
    ˆ Êîíôèãóðèðîâàíèå:
    Åñëè âû õîòü ðàç çàäàâàëèñü âîïðîñîì, ÷òî îçíà÷àþò ðàçëè÷íûå îïöèè â âàøèõ
    êîíôèãóðàöèîííûõ ôàéëàõ, òàêèõ êàê app/config/config.yml, òî ýòîò ðàçäåë
    äëÿ âàñ. Çäåñü âñå êîíôèãóðàöèîííûå ïàðàìåòðû ðàçáèòû íà ïîäðàçäåëû ïî êëþ÷ó (íàïðèìåð framework), êîòîðûå îïðåäåëÿþò âñå âîçìîæíûå ñåêöèè â íàñòðîéêàõ Symfony2.

     framework
     doctrine
     security
     assetic
     swiftmailer
     twig
     monolog
     web_profiler
    ˆ Ôîðìû è âàëèäàöèÿ

    

    Ôîðìû, ñïðàâî÷íèê òèïîâ ïîëåé

     Âàëèäàöèÿ, ñïðàâî÷íèê îãðàíè÷åíèé
    

    Twig: ñïðàâî÷íèê ôóíêöèé äëÿ ðàáîòû ñ ôîðìàìè

    ˆ Îñòàëüíûå ðàçäåëû

    384

    Ãëàâà 4. Reference Documents

    Symfony Documentation, Âûïóñê 2.0

    

    Òàãè Dependency Injection

     /reference/YAML
     /reference/requirements

    4.3.

    Òàãè Dependency Injection

    385

    Symfony Documentation, Âûïóñê 2.0

    386

    Ãëàâà 4. Reference Documents

    ×àñòü V
    Ïàêåòû

    387

    Symfony Documentation, Âûïóñê 2.0

    Symfony Standard Edition ïîñòàâëÿåòñÿ ñ íåñêîëüêèìè ïàêåòàìè. Óçíàéòå î íèõ áîëüøå:

    389

    Symfony Documentation, Âûïóñê 2.0

    390

    Ãëàâà

    5

    Symfony SE Bundles
    ˆ SensioFrameworkExtraBundle
    ˆ SensioGeneratorBundle
    ˆ JMSSecurityExtraBundle
    ˆ DoctrineFixturesBundle
    ˆ DoctrineMigrationsBundle
    ˆ DoctrineMongoDBBundle
    ˆ SensioFrameworkExtraBundle
    ˆ SensioGeneratorBundle
    ˆ JMSSecurityExtraBundle
    ˆ DoctrineFixturesBundle
    ˆ DoctrineMigrationsBundle
    ˆ DoctrineMongoDBBundle

    391

    Symfony Documentation, Âûïóñê 2.0

    392

    Ãëàâà 5.

    Symfony SE Bundles

    ×àñòü VI
    Ó÷àñòèå â ïðîåêòå

    393

    Symfony Documentation, Âûïóñê 2.0

    Ïðèìèòå ó÷àñòèå â ðàçâèòèè Symfony2:

    395

    Symfony Documentation, Âûïóñê 2.0

    396

    Ãëàâà

    6

    Ñîäåéñòâèå
    Ïîìîùü â ðàçðàáîòêå Symfony2:

    6.1 Ñîäåéñòâèå â êîäå
    6.1.1 Ñîîáùåíèå îá îøèáêå

    Êîãäà íàõîäèòå îøèáêó â Symfony2, ëþáåçíî ïðîñèì âàñ ñîîáùèòü î íåé. Ýòî ïîìîæåò
    ñäåëàòü Symfony2 ëó÷øå.

    Îñòîðîæíî: Åñëè âû ïîëàãàåòå ÷òî íàøëè áðåøü â áåçîïàñíîñòè, âîñïîëüçóéòåñü
    ñïåöèàëüíîé

    ïðîöåäóðîé .

    Ïåðåä îòïðàâêîé îøèáêè:
    ˆ Äâàæäû ïðîâåðüòå îôèöèàëüíóþ documentation ÷òîáû óäîñòîâåðèòüñÿ ÷òî âû
    ïðàâèëüíî èñïîëüçóåòå ôðåéìâîðê;
    ˆ Ïîïðîñèòå ñîäåéñòâèÿ â users mailing-list, íà forum èëè #symfony IRC channel åñëè
    íå óâåðåíû â òîì ÷òî ýòî äåéñòâèòåëüíî îøèáêà.
    Åñëè ïðîáëåìà áåç ñîìíåíèé âûãëÿäèò êàê îøèáêà, ñîîáùèòå î íåé, èñïîëüçóÿ tracker
    è ñîáëþäàÿ íåñêîëüêî îñíîâíûõ ïðàâèë:
    ˆ Èñïîëüçóéòå ïîëå title ÷òîáû ÷¼òêî îáîçíà÷èòü âîïðîñ;
    ˆ Îïèøèòå øàãè, íåîáõîäèìûå äëÿ âîñïðîèçâåäåíèÿ îøèáêè ñ êîðîòêèìè ïðèìåðàìè êîäà (åù¼ ëó÷øå ïðåäîñòàâèòü ìîäóëüíûé òåñò, ïîêàçûâàþùèé å¼);
    ˆ Ïðåäîñòàâüòå êàê ìîæíî áîëåå äåòàëüíóþ èíôîðìàöèþ î ñâî¼ì îêðóæåíèè (ÎÑ,
    âåðñèÿ PHP, âåðñèÿ Symfony, âêëþ÷¼ííûå ðàñøèðåíèÿ, ...);

    397

    Symfony Documentation, Âûïóñê 2.0

    ˆ

    (ïî óñìîòðåíèþ)

    Ïðèêðåïèòå ïàò÷.

    6.1.2 Ñîîáùåíèå î áðåøè â áåçîïàñíîñòè

    Íàøëè ïðîáëåìó â áåçîïàñíîñòè Symfony2? Íå èñïîëüçóéòå mailing-list èëè áàã òðåêåð. Âìåñòî íèõ âñå ïðîáëåìû áåçîïàñíîñòè äîëæíû îòïðàâëÿòüñÿ íà security [at]
    symfony-project.com. Ïèñüìà, îòïðàâëåííûå òóäà, ïåðåíàïðàâëÿþòñÿ â çàêðûòûé
    ñïèñîê ðàññûëêè äëÿ ðàçðàáîò÷èêîâ Symfony.
    Äëÿ êàæäîãî ñîîáùåíèÿ ìû ïûòàåìñÿ ïîäòâåðäèòü óÿçâèìîñòü. Êîãäà îíà ïîäòâåðäèòñÿ, ðàçðàáîò÷èêè òðóäÿòñÿ íàä å¼ ðåøåíèåì, ñîãëàñíî ýòèì øàãàì:
    1. Îòïðàâëÿþò ïîäòâåðæäåíèå îòïðàâèòåëþ;
    2. Ðàáîòàþò íàä ïàò÷åì;
    3. Îïèñûâàþò óÿçâèìîñòü, âîçìîæíûå àòàêè è êàê çàêðûòü/ìîäåðíèçèðîâàòü ïîñòðàäàâøèå ïðèëîæåíèÿ;
    4. Ïðèìåíÿþò ïàò÷ êî âñåì ïîääåðæèâàåìûì âåðñèÿì Symfony;
    5. Ïóáëèêóþò ñîîáùåíèå íà îôèöèàëüíîì áëîãå Symfony.

    Ïðèìå÷àíèå: Ïîêà ìû ðàáîòàåì íàä ïàò÷åì, ïîæàëóéñòà, íå ðàçãëàøàéòå ïðîáëåìó.

    6.1.3 Ñîãëàøåíèÿ

    Äîêóìåíò ñòàíäàðòû îïèñûâàåò ñòàíäàðòû êîäèðîâàíèÿ äëÿ ïðîåêòà Symfony2, åãî
    âíóòðåííèõ è ñòîðîííèõ áàíäëîâ. Ýòîò äîêóìåíò îïèñûâàåò ñòàíäàðòû êîäèðîâàíèÿ
    è ñîãëàøåíèÿ, èñïîëüçóåìûå â ÿäðå ôðåéìâîðêà ÷òîáû ñäåëàòü åãî áîëåå ïîñëåäîâàòåëüíûì è ïðåäñêàçóåìûì. Ìîæåòå ñëåäîâàòü èì â ñâî¼ì êîäå, íî â ýòîì íåò îñîáîé
    íóæäû.
    Èìåíà ìåòîäîâ

    Êîãäà îáúåêò èìååò ãëàâíóþ ìíîæåñòâåííóþ ñâÿçü ñî ñâÿçàííûìè ïðåäìåòàìè (îáúåêòû, ïàðàìåòðû è ò. ä.), èìåíà ìåòîäîâ ñòàíäàðòèçóþòñÿ:
    ˆ get()
    ˆ set()
    ˆ has()
    ˆ all()

    398

    Ãëàâà 6.

    Ñîäåéñòâèå

    Symfony Documentation, Âûïóñê 2.0

    ˆ replace()
    ˆ remove()
    ˆ clear()
    ˆ isEmpty()
    ˆ add()
    ˆ register()
    ˆ count()
    ˆ keys()
    Èñïîëüçîâàíèå ýòèõ ìåòîäîâ ïðèìåíèìî òîëüêî êîãäà ÿñíî ÷òî ñóùåñòâóåò îñíîâíàÿ
    ñâÿçü:
    ˆ CookieJar èìååò ìíîæåñòâî Cookie;
    ˆ Ñëóæáà Container èìååò ìíîæåñòâî ñëóæá è ìíîæåñòâî ïàðàìåòðîâ (ò. ê. ñëóæáû
    ýòî ãëàâíàÿ ñâÿçü, òî ìû èñïîëüçóåì ñîãëàøåíèÿ äëÿ èì¼í äëÿ ýòîé ñâÿçè);
    ˆ Êîíñîëü Input èìååò ìíîæåñòâî àðãóìåíòîâ è ìíîæåñòâî îïöèé. Çäåñü íåò îñíîâíîé ñâÿçè, ïîýòîìó ñîãëàøåíèÿ äëÿ èì¼í íå ïðèìåíÿþòñÿ.
    Äëÿ ìíîæåñòâåííûõ ñâÿçåé, ê êîòîðûì íå ïðèìåíÿåòñÿ ñîãëàøåíèå, äîëæíû èñïîëüçîâàòüñÿ ñëåäóþùèå ìåòîäû (ãäå XXX ýòî èìÿ ñîîòâåñòâóþùåãî ïðåäìåòà):

    Ãëàâíàÿ ñâÿçü

    get()
    set()
    has()
    all()
    replace()
    remove()
    clear()
    isEmpty()
    add()
    register()
    count()
    keys()

    Äðóãèå ñâÿçè

    getXXX()
    setXXX()
    hasXXX()
    getXXXs()
    setXXXs()
    removeXXX()
    clearXXX()
    isEmptyXXX()
    addXXX()
    registerXXX()
    countXXX()
    íå äîñòóïíî

    6.1.4 Ëèöåíçèÿ Symfony2

    Symfony2 âûïóùåí ïîä ëèöåíçèåé MIT.
    Ñîãëàñíî Wikipedia:

    6.1.

    Ñîäåéñòâèå â êîäå

    399

    Symfony Documentation, Âûïóñê 2.0

    Ýòî ðàçðåøàþùàÿ ëèöåíçèÿ, îçíà÷àåò ÷òî îíà ðàçðåøàåò èñïîëüçîâàíèå â
    ïðîïðèåòàðíûõ ïðîãðàììàõ, ïðè óñëîâèè ÷òî ëèöåíçèÿ ðàñïðîñòðàíÿåòñÿ ñ
    ýòîé ïðîãðàììîé. Òàêæå îíà GPL-ñîâìåñòèìà, ýòî çíà÷èò ÷òî GPL ðàçðåøàåò êîìáèíèðîâàíèå è ðàñïðîñòðàíåíèå ñ ïðîãðàììàìè, èñïîëüçóþùèìè
    ëèöåíçèþ MIT.
    Ëèöåíçèÿ

    Copyright (c) 2004-2010 Fabien Potencier
    Permission is hereby granted, free of charge, to any person obtaining a copy of this
    software and associated documentation les (the Software), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.
    THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY
    KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
    AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

    6.2 Ñîäåéñòâèå â äîêóìåíòàöèè
    6.2.1 Ñîäåéñòâèå â äîêóìåíòàöèè

    Äîêóìåíòàöèÿ òàêæå âàæíà êàê è êîä. Îíà ñëåäóåò òåì æå ïðèíöèïàì: DRY, òåñòû,
    ïðîñòîòà â îáñëóæèâàíèè, ðàñøèðÿåìîñòü, îïòèìèçàöèÿ è ðåôàêòîðèíã âîò íåêîòîðûå
    èç íèõ. È êîíå÷íî æå äîêóìåíòàöèÿ èìååò îøèáêè, îïå÷àòêè, òðóäíî÷èòàåìûå èíñòðóêöèè è ò. ä.
    Ñîäåéñòâèå

    Ïåðåä ñîäåéñòâèåì âàì íåîáõîäèìî îçíàêîìèòüñÿ ñ
    äîêóìåíòàöèè.

    ÿçûêîì ðàçìåòêè ,

    èñïîëüçóåìûì â

    Äîêóìåíòàöèÿ Symfony2 ðàñïîëàãàåòñÿ â Git ðåïîçèòîðèè:
    400

    Ãëàâà 6.

    Ñîäåéñòâèå

    Symfony Documentation, Âûïóñê 2.0

    git://github.com/symfony/symfony-docs.git

    Åñëè âû õîòèòå ïðèñëàòü ïàò÷, êëîíèðóéòå îôèöèàëüíûé ðåïîçèòîðèé ñ äîêóìåíòàöèåé:
    $ git clone git://github.com/symfony/symfony-docs.git

    Ïðèìå÷àíèå: Äîêóìåíòàöèÿ Symfony2 âûõîäèò ïîä ëèöåíçèåé Creative Commons
    Attribution-Share Alike 3.0 Unported.

    Ñîîáùåíèå îá îøèáêå

    Ñàìîå ïðîñòîå ñîäåéñòâèå ÷òî âû ìîæåòå îêàçàòü ýòî ñîîáùåíèå îá: îïå÷àòêå, ãðàììàòè÷åñêîé îøèáêå, îøèáêå â ïðèìåðå êîäà, îòñóòñòâóþùåì ïîÿñíåíèè è ò. ä.
    Øàãè:
    ˆ Ñîîáùèòü îá îøèáêå â áàã òðåêåð;
    ˆ

    (ïî æåëàíèþ)

    Ïðèñëàòü ïàò÷.

    Ïåðåâîä

    Ïðî÷èòàéòå ïîñâÿù¼ííûé ýòîìó äîêóìåíò.
    6.2.2 Ôîðìàò äîêóìåíòàöèè

    Äîêóìåíòàöèÿ Symfony2 èñïîëüçóåò reStructuredText êàê ÿçûê ðàçìåòêè è Sphinx äëÿ
    ñîçäàíèÿ âûâîäà (HTML, PDF è ò. ä.).
    reStructuredText

    reStructuredText ýòî ëåãêî÷èòàåìûé, ÷òî âèäèøü òî è ïîëó÷èøü, ñèíòàêñèñ ðàçìåòêè
    îòêðûòûì òåêñòîì è ñèñòåìà àíàëèçà.
    Óçíàéòå áîëüøå î åãî ñèíòàêñèñå, ïðî÷èòàâ Symfony2 documents èëè reStructuredText
    Primer íà web ñàéòå Sphinx.
    Åñëè âû çíàêîìû ñ Markdown, áóäüòå îñòîðîæíû, ò. ê. íåêîòîðûå âåùè î÷åíü çíàêîìû,
    íî îòëè÷àþòñÿ:
    ˆ Ñïèñêè íà÷èíàþòñÿ ñ íà÷àëà ñòðîêè (íåîáõîäèìîñòü îòñòóïà îòñóòñòâóåò);
    ˆ Âñòðîåííûå áëîêè êîäà èñïîëüçóþò äâîéíûå êàâû÷êè (``êàê çäåñü``).

    6.2.

    Ñîäåéñòâèå â äîêóìåíòàöèè

    401

    Symfony Documentation, Âûïóñê 2.0

    Sphinx

    Sphinx - ýòî ñèñòåìà ñáîðêè, äîáàâëÿþùàÿ ïîëåçíûå èíñòðóìåíòû äëÿ ñîçäàíèÿ äîêóìåíòàöèè èç äîêóìåíòîâ reStructuredText. Îíà äîáàâëÿåò óêàçàíèÿ è ðîëè èíòåðïðåòèðîâàííîãî òåêñòà ê ñòàíäàðòíîé reST markup.

    Ïîäñâåòêà ñèíòàêñèñà
    Âñå ïðèìåðû êîäà ïîäñâå÷èâàþòñÿ ïî óìîë÷àíèþ êàê ÿçûê PHP. Âû ìîæåòå èçìåíèòü
    èõ ÷åðåç äèðåêòèâó code-block:
    .. code-block:: yaml
    { foo: bar, bar: { foo: bar, bar: baz } }

    Åñëè âàø PHP êîä íà÷èíàåòñÿ ñ ïñåâäî-ÿçûê:
    .. code-block:: html+php
    foobar(); ?>

    Ïðèìå÷àíèå: Ñïèñîê ïîääåðæèâàåìûõ ÿçûêîâ äîñòóïåí íà Pygments website.
    Áëîêè êîíôèãóðàöèé
    Âñÿêèé

    ðàç

    êàê

    âû ïîêàçûâàåòå êîíôèãóðàöèþ, èñïîëüçóéòå äèðåêòèâó
    configuration-block ÷òîáû îòðàçèòü êîíôèãóðàöèþ âî âñåõ ïîääåðæèâàåìûõ
    ôîðìàòàõ (PHP, YAML è XML):
    .. configuration-block::
    .. code-block:: yaml
    # Configuration in YAML
    .. code-block:: xml

    .. code-block:: php
    // Configuration in PHP

    402

    Ãëàâà 6.

    Ñîäåéñòâèå

    Symfony Documentation, Âûïóñê 2.0

    Ïðåäûäóùàÿ reST ðàçìåòêà îòîáðàçèòñÿ ñëåäóþùèì îáðàçîì:
    ˆ

    YAML

    # Configuration in YAML

    ˆ

    XML



    ˆ

    PHP

    // Configuration in PHP

    Òåêóùèé ñïèñîê ïîääåðæèâàåìûõ ôîðìàòîâ:

    Ôîðìàò ðàçìåòêè

    html
    xml
    php
    yaml
    jinja
    html+jinja
    jinja+html
    php+html
    html+php
    ini
    php-annotations

    Îòîáðàæàåòñÿ

    HTML
    XML
    PHP
    YAML
    Twig
    Twig
    Twig
    PHP
    PHP
    INI
    Àííîòàöèè

    6.3 Ñîäåéñòâèå â êîäå
    6.3.1 Äðóãèå èñòî÷íèêè

    ×òîáû áûòü â êóðñå òîãî ÷òî ïðîèñõîäèò â ñîîáùåñòâå, âàì ìîãóò ïðèãîäèòüñÿ ýòè
    äîïîëíèòåëüíûå èñòî÷íèêè:
    ˆ Ñïèñîê îòêðûòûõ pull requests
    ˆ Ñïèñîê ïîñëåäíèõ commits
    ˆ Ñïèñîê îòêðûòûõ bugs
    ˆ Ñïèñîê open source bundles
    ˆ Êîä:

    

    Îøèáêè

    |

     수֏ |
    6.3.

    Ñîäåéñòâèå â êîäå

    403

    Symfony Documentation, Âûïóñê 2.0

    

    Áåçîïàñíîñòü

    |

     Òåñòû |
     Ïðàâèëà íàïèñàíèÿ êîäà |
    

    Äîãîâîð¼ííîñòè ïî êîäó

    

    Ëèöåíçèÿ

    |

    ˆ Äîêóìåíòàöèÿ:

    

    Îáçîð

    

    Ôîðìàò

    |
    |

     Ïåðåâîäû |
     Ëèöåíçèÿ
    ˆ Ñîîáùåñòâî:

     Ñîâåùàíèÿ â IRC |
    

    Äðóãèå èñòî÷íèêè

    ˆ Êîä:

    

    Îøèáêè

    |

     수֏ |
    

    Áåçîïàñíîñòü

    |

     Òåñòû |
     Ïðàâèëà íàïèñàíèÿ êîäà |
    

    Äîãîâîð¼ííîñòè ïî êîäó

    

    Ëèöåíçèÿ

    |

    ˆ Äîêóìåíòàöèÿ:

    

    Îáçîð

    

    Ôîðìàò

    |
    |

     Ïåðåâîäû |
     Ëèöåíçèÿ
    ˆ Ñîîáùåñòâî:

     Ñîâåùàíèÿ â IRC |
    

    404

    Äðóãèå èñòî÷íèêè

    Ãëàâà 6.

    Ñîäåéñòâèå

    Àëôàâèòíûé óêàçàòåëü
    Symbols

    Êåøèðîâàíèå
    Varnish, 344
    Àâòîçàãðóç÷èê
    Êýø
    Êîíôèãóðèðîâàíèå, 356
    Twig, 101
    Ôîðìû, 179
    Êýøèðîâàíèå,
    246
    Àâòîìàòè÷åñêîå îïðåäåëåíèå òèïîâ ïîÁåçîïàñíûå ìåòîäû, 253
    ëåé, 190, 191
    Êîíôèãóðàöèÿ, 261
    Äèçàéí, 200
    Îáðàòíûé ïðîêñè, 248
    Èìåíîâàíèå ôðàãìåíòîâ ôîðì, 202
    Îáðàòíûé ïðîêñè Symfony2, 249
    Êàñòîìèçàöèÿ ïîëåé, 200
    Ïðîêñè, 248
    Íàñëåäîâàíèå ôðàãìåíòîâ øàáëîíà,
    Øëþç, 248
    202
    Òèïû, 248
    Îáðàáîòêà îòïðàâêè ôîðì, 183
    Óñëîâíûé Get, 259
    Îïöèè ïîëåé ôîðì, 189
    Âàëèäàöèÿ, 256
    Îòîáðàæåíèå ôîðìû â øàáëîíå, 181
    Âàðèàöèè, 260
    Îòîáðàæåíèå êàæäîãî ïîëÿ âðó÷íóþ,
    Çàãîëîâîê Cache-Control, 252, 256
    193
    Çàãîëîâîê Etag, 257
    Îòîáðàæåíèå â øàáëîíå, 192
    Çàãîëîâîê Expires, 255
    Ïîëÿ
    Çàãîëîâîê Last-Modied, 258
    collection, 371
    ESI, 261
    date, 371
    HTTP, 251
    Ñîçäàíèå ôîðìû â êîíòðîëëåðå, 180
    HTTP Expiration - îêîí÷àíèå ñòðîêà
    Ñîçäàíèå êëàññîâ ôîðì, 195
    äåéñòâèÿ, 255
    Ñîçäàíèå ïðîñòîé ôîðìû, 179
    Êîíôèãóðàöèÿ
    Ñïðàâî÷íèê ôóíêöèé Twig, 375
    Êýøèðîâàíèå, 261
    Ñïðàâî÷íèê òèïîâ ïîëåé, 371
    Òåñòû, 161
    Âàëèäàöèîííûå ãðóïïû, 186
    PHPUnit, 161
    Âàëèäàöèÿ, 184
    Êîíôèãóðèðîâàíèå
    Âñòðàèâàíèå ôîðì, 197
    Àâòîçàãðóç÷èê, 356
    Âñòðîåííûå òèïû ïîëåé, 188
    Âàëèäàöèÿ, 166
    Çàùèòà îò CSRF àòàê, 206
    Êîíòåéíåð
    ñëóæá, 283
    Doctrine, 196
    405

    Symfony Documentation, Âûïóñê 2.0

    ×òî òàêîå êîíòåéíåð ñëóæá?, 284
    ×òî òàêîå ñëóæáà?, 284
    Èìïîðò, 289
    Íàñòðîéêà ñëóæá, 285
    Ðàñøèðåíèå êîíôèãóðàöèè, 291
    Âíåäðåíèå ñëóæá, 293
    Êîíòåéíåð âíåäðåíèÿ çàâèñèìîñòè, 283
    Êîíòðîëëåð, 61
    Àðãóìåíòû êîíòðîëëåðà, 65
    Áàçîâûå îïåðàöèè, 68
    Áàçîâûé êëàññ êîíòðîëëåðà, 67
    Äîñòóï ê ñåðâèñàì, 71
    Ìàðøðóòû è êîíòðîëëåðû, 64
    Îáúåêò îòâåòà, 74
    Îáúåêò çàïðîñà, 74
    Ïåðåàäðåñàöèÿ, 70
    Ïåðåíàïðàâëåíèå, 69
    Ïðîñòîé ïðèìåð, 63
    Ðàçáèðàåìñÿ ñ îøèáêàìè, 72
    Ñåññèè, 72
     êà÷åñòâå ñåðâèñà, 335
    Æèçíåííûé öèêë Çàïðîñ-ÊîíòðîëëåðÎòâåò, 62
    ðåíäåðèíã øàáëîíîâ, 71
    404 ñòðàíèöà, 72
    Êîíòðîëëåðû
    Ôîðìàò Èìåíîâàíèÿ, 91
    Ìàðøðóòèçàöèÿ, 75
    Àáñîëþòíûå URL, 96
    ×òî ïîä êàïîòîì, 77
    Ãåíåðàöèÿ URL, 96
    Ãåíåðàöèÿ URL â øàáëîíå, 97
    Êîíòðîëëåðû, 91
    Îãðàíè÷åíèÿ, 84
    Îãðàíè÷åíèÿ äëÿ HTTP-ìåòîäà, 87
    Îñíîâû, 76
    Îòëàäêà, 95
    Ïîäêëþ÷åíèå âíåøíèõ ðåñóðñîâ, 92
    Ïðîäâèíóòûå ïðèìåðû èñïîëüçîâàíèÿ,
    89
    Ñîçäàíèå ìàðøðóòîâ, 78
    Ññûëêè íà ñòðàíèöû, 110
    Çàïîëíèòåëè, 80
    Îêðóæåíèÿ
    Êîíôèãóðàöèÿ, 59
    406

    Ââåäåíèå, 58
    Îñíîâû Symfony2, 11
    Îñíîâû Symfony2 Fundamentals
    Çàïðîñû è îòâåòû, 15
    Ïåðåâîäû, 267
    Äîìåíû ñîîáùåíèé, 276
    Êàòàëîãè ñîîáùåíèé, 271
    Ëîêàëü ïîëüçîâàòåëÿ, 276
    Ìíîæåñòâåííîå ÷èñëî, 278
    Íàñòðîéêà, 268
    Îñíîâû ïåðåâîäîâ, 268
    Ðàñïîëîæåíèå ðåñóðñîâ ïåðåâîäà, 272
    Ñîçäàíèå ðåñóðñîâ ñ ïåðåâîäàìè, 273
    Â øàáëîíàõ, 280
    Çàïîëíèòåëè â ñîîáùåíèÿõ, 270
    Ïîèñêîâèê (Finder), 359
    Ñêðèïòû Javascript
    Ïîäêëþ÷åíèå Javascript ôàéëîâ, 113
    Ñîçäàíèå ñòðàíèö, 42
    Ñîçäàíèå ñòðàíèöû
    Ïðèìåð, 42
    Ñòèëè
    Ïîäêëþ÷åíèå CSS ôàéëîâ, 113
    Ñòðóêòóðà äèðåêòîðèé, 50
    Øàáëîíèçàòîð, 98
    ×òî òàêîå øàáëîí?, 98
    Ýêðàíèðîâàíèå, 118
    Ôîðìàòû, 119
    Õåëïåðû, 107
    Íàñëåäîâàíèå, 101
    Ïîäêëþ÷åíèå CSS è Javascript ôàéëîâ,
    113
    Ïîäêëþ÷åíèå äðóãèõ øàáëîíîâ, 107
    Ïðàâèëà èìåíîâàíèÿ, 105
    Ðàñïîëîæåíèå ôàéëîâ, 105
    Ñåðâèñ øàáëîíèçàòîðà, 114
    Ññûëêè íà ðåñóðñû, 112
    Òàãè è Õåëïåðû, 107
    Òð¼õóðîâíåâîå íàñëåäîâàíèå, 117
    Âíåäðåíèå êîíòðîëëåðîâ, 109
    Òåñòû, 148, 342
    Ôóíêöèîíàëüíûå òåñòû, 150
    Êëèåíò, 154
    Êîíôèãóðàöèÿ, 161
    Ìîäóëüíûå òåñòû, 148
    Àëôàâèòíûé óêàçàòåëü

    Symfony Documentation, Âûïóñê 2.0

    Ïðîôèëèðîâàíèå, 343
    Crawler, 157
    HTTP Àóòåíòèôèêàöèÿ, 342
    Óñòàíîâêà, 37
    Âàëèäàöèÿ, 162
    Öåëè äëÿ îãðàíè÷åíèé, 172
    Èñïîëüçîâàíèå âàëèäàòîðà, 164
    Êîíôèãóðàöèÿ îãðàíè÷åíèé, 169
    Êîíôèãóðèðîâàíèå, 166
    Îãðàíè÷åíèÿ, 167
    Îãðàíè÷åíèÿ äëÿ ìåòîäîâ, 173
    Îãðàíè÷åíèÿ äëÿ ïîëåé êëàññà, 172
    Âàëèäàöèÿ ôîðì, 166
    Âàëèäàöèÿ îáû÷íûõ çíà÷åíèé, 178
    Æóðíàëèðîâàíèå, 352

    C

    Cache
    Invalidation, 265
    CLI
    Doctrine ORM, 147

    D

    Doctrine, 121
    Ôîðìû, 196
    Adding mapping metadata, 123
    ORM Console Commands, 147

    E

    Emails, 338
    Gmail, 341
    ESI, 261
    Event
    Kernel, 308
    kernel.controller, 309
    kernel.exception, 311
    kernel.request, 309
    kernel.response, 310
    kernel.view, 309
    Event Dispatcher, 311
    Creating and Dispatching an Event, 315
    Event Subclasses, 312
    Event subscribers, 318
    Events, 312
    Listeners, 313
    Àëôàâèòíûé óêàçàòåëü

    Naming conventions, 312
    Stopping event ow, 319

    F

    Forms
    Global Theming, 203

    H

    HTTP
    Ïðèíöèï çàïðîñ-îòâåò, 12
    304, 259
    HTTP çàãîëîâêè
    Cache-Control, 252, 256
    Etag, 257
    Expires, 255
    Last-Modied, 258
    Vary, 260

    I

    Internals, 304
    Controller Resolver, 306
    Internal Requests, 308
    Kernel, 306
    Request Handling, 307

    K

    Kernel
    Event, 308

    L

    Layout, 348

    N

    Naming conventions
    Event Dispatcher, 312

    P

    PHP Templates, 347
    PHPUnit
    Êîíôèãóðàöèÿ, 161
    Proler, 320
    Using the proler service, 321
    Visualizing, 321, 322

    R

    Routing
    407

    Symfony Documentation, Âûïóñê 2.0

    _format parameter, 89
    Scheme requirement, 336

    S

    Service Container
    Advanced conguration, 300
    Session, 72
    single
    Øàáëîíèçàòîð
    Ïåðåîïðåäåëåíèå øàáëîíîâ, 116
    Ïåðåîïðåäåëåíèå øàáëîíîâ äëÿ èñêëþ÷åíèé, 117
    single Ñåññèÿ
    Flash-ñîîáùåíèÿ, 73
    Slot, 349
    Symfony2 Components, 20

    T

    Templating
    Embedding Pages, 350
    Global variables, 346
    Helpers, 351
    Include, 350
    Layout, 348
    Slot, 349
    Tests
    Assertions, 153
    Translations
    Fallback and default locale, 276
    Twig
    Êýø, 101
    Ââåäåíèå, 99

    V

    Varnish
    Àííóëèðîâàíèå, 345
    íàñòðîéêà, 345

    408

    Àëôàâèòíûé óêàçàòåëü