Diferència entre revisions de la pàgina «NF2 - Framework PHP (15h)»
(→Creació i configuració de l'entorn de treball) |
(→Enllaçant pàgines) |
||
(91 revisions intermèdies per 2 usuaris que no es mostren) | |||
Línia 73: | Línia 73: | ||
[[fitxer:mvc-symfony.png]] | [[fitxer:mvc-symfony.png]] | ||
+ | |||
+ | |||
+ | =Instal·lació de Symfony= | ||
+ | Utilitzarem la versió de symfony 2.4. | ||
+ | |||
+ | [[Instal·lació de la versió 2.4 de symfony ]] (antic) | ||
+ | |||
+ | [[Instal·lació de la versió LTS 2.8 de symfony ]] | ||
+ | |||
+ | [http://symfony.com/doc/current/index.html Llibre gratuït de symfony2]. | ||
+ | |||
+ | Accedeix a la URL: | ||
+ | http://localhost/m7/web/app.php | ||
+ | |||
+ | '''nota:''' m7 és, en aquest cas, el nomProjecteSymfony utilitzat en la creació del projecte symfony. | ||
+ | |||
+ | Hauries de veure una pàgina com aquesta: | ||
+ | |||
+ | [[Fitxer:symfony2.8_inici.png | 400px]] | ||
+ | |||
+ | ==Creació de la primera pàgina:== | ||
+ | Una vegada executis la línia següent anirà preguntant opcions. Tot per defecte. Al nom del bundle podeu posar: HolaMonBundle | ||
+ | <pre> | ||
+ | php app/console generate:bundle --namespace=m7/HolaMon --format=yml | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | ==Iniciar el servidor == | ||
+ | Symfony pot ser instal·lat dintre d'un servidor apache2 però per entorns de desenvolupament porta un servidor web integrat. Per executar el servidor i poder provar les aplicacions podeu iniciar-lo amb la comanda de terminal: | ||
+ | <pre> | ||
+ | php app/console server:run | ||
+ | </pre> | ||
+ | Si heu engegat el servidor integrat podeu provar la pàgina creada en el punt anterior utilitzant aquesta [http://localhost:8000/ url] | ||
+ | |||
+ | Si teniu apache2 instal·lat, podeu provar la pàgina utilitzant aquesta [http://localhost/m7/web/app.php/ url_apache_m7] | ||
+ | == Si hi ha un error accedint a la pàgina!!== | ||
+ | Amb apache2, si en algun moment deixa de funcionar heu de provar de fer: | ||
+ | <pre> | ||
+ | sudo php app/console cache:clear --env=prod && sudo php app/console cache:warmup --env=prod | ||
+ | </pre> | ||
+ | i donar-li permisos (una altre vegada) a la carpeta principal (dintre de /var/www/html): | ||
+ | <pre> | ||
+ | chmod 777 nomprojecte/ -R | ||
+ | </pre> | ||
+ | |||
+ | ===Exercici [opcional]:=== | ||
+ | Utilitzant el servidor web apache2 creeu un virtualhost per poder utilitzar symfony2 directament posant: http://localhost/ | ||
=Estructura del projecte: Aplicacions, Mòduls i Accions= | =Estructura del projecte: Aplicacions, Mòduls i Accions= | ||
Línia 86: | Línia 133: | ||
Els mòduls emmagatzemen les accions, que representen cadascuna de les operacions que es pot realitzar en un mòdul. Per exemple el mòdul ''carretCompra'' pot definir accions com ''afegir'', mostrar i actualitzar. Normalment les accions es descriuen mitjançant verbs. Treballar amb accions és molt similar a treballar amb les pàgines d'una aplicació web tradicional, encara que en aquest cas dues accions diferents poden acabar mostrant la mateixa pàgina (com per exemple l'acció d'afegir un comentari a una entrada d'un blog, que acaba tornant a mostrar la pàgina de l'entrada amb el nou comentari). | Els mòduls emmagatzemen les accions, que representen cadascuna de les operacions que es pot realitzar en un mòdul. Per exemple el mòdul ''carretCompra'' pot definir accions com ''afegir'', mostrar i actualitzar. Normalment les accions es descriuen mitjançant verbs. Treballar amb accions és molt similar a treballar amb les pàgines d'una aplicació web tradicional, encara que en aquest cas dues accions diferents poden acabar mostrant la mateixa pàgina (com per exemple l'acció d'afegir un comentari a una entrada d'un blog, que acaba tornant a mostrar la pàgina de l'entrada amb el nou comentari). | ||
=Estructura de l'arbre de arxius= | =Estructura de l'arbre de arxius= | ||
− | + | Normalment, tots els projectes web comparteixen el mateix tipus de continguts, com per exemple: | |
Una base de dades, com MySQL o PostgreSQL | Una base de dades, com MySQL o PostgreSQL | ||
Línia 99: | Línia 146: | ||
Symfony proporciona una estructura en forma d'arbre d'arxius per organitzar de forma lògica tots aquests continguts, a més de ser consistent amb l'arquitectura MVC utilitzada i amb l'agrupació projecto / aplicació / mòdul. Cada vegada que es crea un nou projecte, aplicació o mòdul, es genera de forma automàtica la part corresponent d'aquesta estructura. A més, l'estructura es pot personalitzar completament, per reorganitzar els arxius i directoris o per complir amb les exigències d'organització d'un client. | Symfony proporciona una estructura en forma d'arbre d'arxius per organitzar de forma lògica tots aquests continguts, a més de ser consistent amb l'arquitectura MVC utilitzada i amb l'agrupació projecto / aplicació / mòdul. Cada vegada que es crea un nou projecte, aplicació o mòdul, es genera de forma automàtica la part corresponent d'aquesta estructura. A més, l'estructura es pot personalitzar completament, per reorganitzar els arxius i directoris o per complir amb les exigències d'organització d'un client. | ||
− | + | Encara que es pot canviar, per defecte totes les aplicacions Symfony tenen la mateixa estructura de directoris senzilla (i recomanada): | |
+ | *'''app/''': conté la configuració de l'aplicació. | ||
+ | *'''src/''': aquí es troba tot el codi PHP de l'aplicació. | ||
+ | *'''vendor/''': per convenció aquí es guarden totes les llibreries creades per tercers. | ||
+ | *'''web/''': est és el directori web arrel i conté tots els arxius que es poden accedir públicament. | ||
+ | ==El directori web== | ||
+ | El directori web arrel és el lloc on es troben tots els arxius públics i estàtics tals com a imatges, fulles d'estil i arxius Javascript. També és el lloc on es defineixen tots els controladors frontals, com per exemple el següent: | ||
+ | <source lang="php"> | ||
+ | // web/app.php | ||
+ | require_once __DIR__.'/../app/bootstrap.php.cache'; | ||
+ | require_once __DIR__.'/../app/AppKernel.php'; | ||
+ | |||
+ | use Symfony\Component\HttpFoundation\Request; | ||
+ | |||
+ | $kernel = new AppKernel('prod', false); | ||
+ | $kernel->loadClassCache(); | ||
+ | $kernel->handle(Request::createFromGlobals())->send(); | ||
+ | </source> | ||
+ | L'arxiu del controlador frontal ('''app.php''' en aquest exemple) és l'arxiu PHP que realment s'executa quan utilitzes una aplicació Symfony2 i el seu treball consisteix a arrencar l'aplicació utilitzant una classe del nucli ('''AppKernel'''). | ||
− | + | Tenir un controlador frontal significa que s'utilitzen URL diferents i més flexibles que les d'una aplicació PHP típica. Quan es disposa d'un controlador frontal, les URL es formaten de la següent manera: | |
+ | <pre> | ||
+ | http://localhost/app.php/hello/Ryan | ||
+ | </pre> | ||
+ | El controlador frontal, '''app.php''', s'executa i la URL interna: ''/hello/Ryan'' es dirigeix internament segons la configuració d'encaminament. | ||
− | + | Si a més utilitzes el mòdul '''mod_rewrite''' d'Apache, pots forçar l'execució de l'arxiu '''app.php''' sense necessitat d'incloure-ho en la URL, per la qual cosa així les URL són encara més netes: | |
− | |||
− | |||
− | |||
− | |||
<pre> | <pre> | ||
− | + | http://localhost/hello/Ryan | |
</pre> | </pre> | ||
+ | ==El directori de l'aplicació (app)== | ||
+ | La classe '''AppKernel''' és el punt d'entrada principal de l'aplicació i és la responsable de tota la configuració. Com a tal, s'emmagatzema en el directori '''app/'''. | ||
− | + | Aquesta classe ha d'implementar dos mètodes que defineixen tot el que Symfony necessita saber sobre la teva aplicació. Ni tan sols has de preocupar-te d'aquests mètodes durant l'arrencada — Symfony els emplena per tu amb paràmetres predeterminats. | |
+ | |||
+ | *'''registerBundles()''': retorna un array amb tots els bundles necessaris per executar l'aplicació. | ||
+ | *'''registerContainerConfiguration()''': carrega l'arxiu de configuració de recursos de l'aplicació (consulta la secció Configurant l'aplicació). | ||
+ | Durant el desenvolupament d'una aplicació, normalment el directori '''app/''' solament els utilitzes per modificar la configuració i els arxius d'encaminament en el directori '''app/config/'''. | ||
+ | |||
+ | Aquest directori també conté el directori '''caché''' de l'aplicació (app/cache), un directori de '''logs''' (app/logs) i un directori per a arxius de '''recursos globals''', tals com a plantilles (app/Resources). | ||
+ | |||
+ | '''Carga automàtica''' | ||
+ | En arrencar Symfony, s'inclou un arxiu especial anomenat ''vendor/autoload.php''. Aquest arxiu, creat per ''Composer'', s'encarrega de configurar el carregador automàtic de classes, que al seu torn '''carrega automàticament tots els arxius''' de la teva aplicació que es trobin '''en el directori src/''' i '''totes les llibreries externes''' configurades en l'arxiu '''composer.json'''. | ||
+ | |||
+ | Gràcies al carregador automàtic, '''mai hauràs de preocupar-te d'usar declaracions include o require'''. Això és possible perquè Composer utilitza namespace o espai de noms d'una classe per determinar la seva ubicació i així '''inclou automàticament l'arxiu en l'instant en què necessites una classe'''. | ||
+ | |||
+ | El carregador automàtic '''ja està configurat per buscar qualsevol de les teves classes PHP en el directori src/'''. Perquè funcioni la càrrega automàtica, el nom de la classe i la ruta de l'arxiu '''han de seguir el mateix patró''': | ||
<pre> | <pre> | ||
− | + | Nom de la classe: Acme\HelloBundle\Controller\HelloController | |
+ | Ruta física de l'arxiu: src/Acme/HelloBundle/Controller/HelloController.php | ||
</pre> | </pre> | ||
+ | == Configuració de l'aplicació == | ||
+ | Segons el lloc web oficial de YAML (http://www.yaml.org/), YAML és "un estàndard per serializar dades en qualsevol llenguatge de programació i amb un format fàcil de llegir per part de les persones". Dit d'una altra forma, YAML és un llenguatge molt senzill que permet descriure les dades com en XML, però amb una sintaxi molt més senzilla. YAML és un format especialment útil per descriure dades que poden ser transformats en arrays simples i associatius, com per exemple: | ||
+ | <source lang="php"> | ||
+ | $casa = array( | ||
+ | 'familia' => array( | ||
+ | 'apellido' => 'García', | ||
+ | 'padres' => array('Antonio', 'María'), | ||
+ | 'hijos' => array('Jose', 'Manuel', 'Carmen') | ||
+ | ), | ||
+ | 'direccion' => array( | ||
+ | 'numero' => 34, | ||
+ | 'calle' => 'Gran Vía', | ||
+ | 'ciudad' => 'Cualquiera', | ||
+ | 'codigopostal' => '12345' | ||
+ | ) | ||
+ | ); | ||
− | + | //format yml: | |
+ | casa: | ||
+ | familia: | ||
+ | apellido: García | ||
+ | padres: | ||
+ | - Antonio | ||
+ | - María | ||
+ | hijos: | ||
+ | - Jose | ||
+ | - Manuel | ||
+ | - Carmen | ||
+ | direccion: | ||
+ | numero: 34 | ||
+ | calle: Gran Vía | ||
+ | ciudad: Cualquiera | ||
+ | codigopostal: "12345" | ||
+ | </source> | ||
+ | YAML és l'acrònim de "YAML Ain't Markup Language" ("YAML No és un Llenguatge de Marcat") i es pronuncia "yamel". El format es porta utilitzant des de 2001 i existeixen utilitats per processar YAML en una gran varietat de llenguatges de programació. | ||
+ | |||
+ | YAML és molt més ràpid d'escriure que XML (ja que no fan falta les etiquetes de tancament i l'ús continu de les cometes) i és molt més poderós que els tradicionals arxius .ini (ja que aquests últims no suporten l'herència i les estructures complexes). Per aquest motiu, Symfony utilitza el format YAML com el llenguatge preferit per emmagatzemar la seva configuració. | ||
+ | |||
+ | Per saber més sobre aquest format pots consultar la [http://symfony.com/legacy/doc/reference/1_4/en/02-YAML següent] pàgina web. | ||
+ | ==El directori font (src)== | ||
+ | El directori '''src/''' conté tot el codi real (codi PHP, plantilles, arxius de configuració, estils, etc.) que pertany a la teva aplicació. De fet, en programar una aplicació Symfony, la major part del teu treball es durà a terme dins d'un o més''' bundles '''creats en aquest directori. | ||
+ | |||
+ | =Els Bundles= | ||
+ | [[Els bundles a symfony 2.]] | ||
+ | |||
+ | =Entorn de desenvolupament i entorn de producció= | ||
+ | Una aplicació pot funcionar en diversos entorns. Els diferents entorns comparteixen el mateix codi PHP (solament és diferent el controlador frontal), però usen una configuració diferent. Per exemple, un entorn de desenvolupament '''dev''' guarda els advertiments i errors, mentre que un entorn de producció '''prod''' només registra els errors. Alguns arxius es tornen a generar en cada petició en l'entorn '''dev''' (para major comoditat dels desenvolupadors), però s'escorcollen en l'entorn '''prod'''. Tots els entorns es troben en la mateixa màquina i executen la mateixa aplicació. | ||
+ | |||
+ | Un projecte Symfony2 '''normalment comença amb tres entorns (dev, test i prod)''', encara que resulta senzill crear nous entorns.''' Pots veure la teva aplicació en diferents entorn''' amb només '''canviar el controlador frontal'''en el teu navegador. Per veure l'aplicació en l'entorn '''dev''', accedeix a l'aplicació a través del controlador frontal de desenvolupament: | ||
+ | <pre> | ||
+ | http://localhost/app_dev.php/hola | ||
+ | </pre> | ||
+ | Si desitges veure com es comportarà la teva aplicació a l'entorn de producció, utilitza en el seu lloc el controlador frontal '''prod''': | ||
+ | <pre> | ||
+ | http://localhost/app.php/hola | ||
+ | </pre> | ||
+ | Si fas qualsevol canvi en les plantilles, no ho veuràs en l'entorn prod tret que esborris la cache de l'aplicació i així forcis a Symfony a tornar a compilar les plantilles. Per esborrar la cache de l'entorn de producció, executa la següent comanda de consola: | ||
<pre> | <pre> | ||
− | php | + | sudo php app/console cache:clear --env=prod && sudo php app/console cache:warmup --env=prod |
+ | i donar-li permísos: | ||
+ | chmod 777 nomprojecte/ -R | ||
</pre> | </pre> | ||
− | + | =El controlador= | |
− | + | Un controlador és una funció PHP creada per tu i que s'encarrega d'obtenir la informació de la petició HTTP i de generar i retornar la resposta HTTP (en forma d'objecte de tipus Response de Symfony2). | |
− | El contingut | + | La resposta pot ser: |
− | + | *una pàgina HTML, | |
− | + | *un document XML, | |
− | + | *un array JSON serializado, | |
− | + | *una imatge, | |
+ | *una redirecció a una altra pàgina, | ||
+ | *un error de tipus 404 | ||
+ | *o qualsevol altra cosa que se t'ocorri. | ||
+ | El controlador conté tota la lògica que la teva aplicació necessita per generar el contingut de la pàgina. L'objectiu d'un controlador sempre és el mateix: crear i retornar un objecte '''Response'''. | ||
+ | ==Cicle de vida d'una petició== | ||
+ | * 1.Cada petició és tractada per un únic arxiu: el controlador frontal (per exemple, '''app.php''' o '''app_dev.php''') el qual és responsable d'iniciar l'aplicació. | ||
+ | * 2. El sistema d'encaminament (classe Routing) llegeix la informació de la petició (per exemple, la URI), troba una ruta que coincideixi amb aquesta informació, i llegeix el paràmetre '''_controller''' de la ruta. | ||
+ | * 3. S'executa el controlador assignat a la ruta i aquest controlador crea i retorna un objecte '''Response'''. | ||
+ | * 4. Les capçaleres HTTP i el contingut de l'objecte Response s'envien de tornada al client. | ||
− | + | Exemple de controlador: | |
− | + | <source lang="php"> | |
+ | // src/Acme/HelloBundle/Controller/HelloController.php | ||
+ | |||
+ | namespace Acme\HelloBundle\Controller; | ||
+ | use Symfony\Component\HttpFoundation\Response; | ||
+ | |||
+ | class HelloController | ||
+ | { | ||
+ | public function indexAction() | ||
+ | { | ||
+ | return new Response('<html><body>Hello world!</body></html>'); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
− | + | ==Associant una URI a un controlador== | |
− | + | El nou controlador retorna una pàgina HTML simple. Per poder provar realment aquesta pàgina en el teu navegador, has de crear una ruta que el seu path sigui la URI que vols associar al controlador al fitxer '''routing.yml''': | |
+ | <source lang="yml"> | ||
+ | hello: | ||
+ | path: /hello | ||
+ | defaults: { _controller: AcmeHelloBundle:Hello:index } | ||
+ | </source> | ||
+ | Observa la sintaxi utilitzada per referir-se al controlador: '''AcmeHelloBundle:Hello:index'''. Symfony2 utilitza aquesta notació curta per referir-se als controladors. Es tracta de la '''sintaxi recomanada''' i li diu a Symfony2 que busqui una '''classe controlador anomenada HelloController''' dins d'un '''paquet''' anomenat''' AcmeHelloBundle '''i que després '''executi el mètode indexAction()'''. | ||
− | + | '''Passar arguments al mètode del controlador''' | |
− | + | Exemple de controlador amb un argument en un dels seus mètodes: | |
− | + | <source lang="php"> | |
+ | <?php | ||
+ | // src/Acme/HelloBundle/Controller/HelloController.php | ||
+ | |||
+ | namespace Acme\HelloBundle\Controller; | ||
+ | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
+ | |||
+ | class HelloController extends Controller | ||
+ | { | ||
+ | public function indexAction($name) | ||
+ | { | ||
+ | // ... | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | El controlador anterior té un sol argument, anomenat '''$name''', el valor del qual correspon al paràmetre '''{name}''' de la ruta associada. De fet, quan executes el teu controlador, Symfony2 associa cada argument del controlador amb un paràmetre de la ruta. | ||
+ | L'arxiu d'enrutament hauria de ser aquest: | ||
+ | <source lang="yml"> | ||
+ | hello: | ||
+ | path: /hello | ||
+ | defaults: { _controller: AcmeHelloBundle:Hello:index } | ||
+ | </source> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Considera el següent exemple: | |
− | + | <source lang="yml"> | |
− | + | # app/config/routing.yml | |
− | + | hello: | |
+ | path: /hello/{firstName}/{lastName} | ||
+ | defaults: { _controller: AcmeHelloBundle:Hello:index, color: green } | ||
+ | </source> | ||
+ | <source lang="php"> | ||
+ | public function indexAction($firstName, $lastName, $color) | ||
+ | { | ||
+ | // ... | ||
+ | } | ||
+ | </source> | ||
+ | Les variables '''{firstName}''' i '''{lastName}''' de la ruta es diuen '''placeholders''', ja que "guarden el lloc" perquè qualsevol valor substitueixi aquesta variable. D'altra banda, la variable '''color''' és una variable de '''tipus default''', ja que el seu valor sempre '''està definit per a totes les rutes'''. | ||
+ | Existeixen regles amb els arguments (paràmetres): | ||
+ | *Cada argument obligatori del controlador ha de tenir associat un paràmetre en la ruta | ||
+ | *No tots els paràmetres de la ruta han de ser arguments en el teu controlador | ||
+ | *és perfectament vàlid fer que l'argument sigui opcional. | ||
− | + | El següent exemple no llançarà una excepció: | |
+ | <source lang="php"> | ||
+ | public function indexAction($firstName, $lastName, $color, $foo = 'bar') | ||
+ | { | ||
+ | // ... | ||
+ | } | ||
+ | </source> | ||
+ | ==objecte Request com a argument del controlador== | ||
+ | Sol ser molt útil disposar en el controlador de l'objecte '''Request''' associat a la petició de l'usuari, especialment quan treballes amb formularis. Per fer que Symfony passi aquest objecte automàticament com a argument del controlador, utilitza el següent codi: | ||
+ | <source lang="php"> | ||
+ | use Symfony\Component\HttpFoundation\Request; | ||
+ | |||
+ | public function updateAction(Request $request) | ||
+ | { | ||
+ | $form = $this->createForm(...); | ||
+ | |||
+ | $form->handleRequest($request); | ||
+ | |||
+ | // ... | ||
+ | } | ||
+ | </source> | ||
− | + | == Generador de controladors== | |
− | + | Si el que volem és generar un nou controlador a la nostra aplicació podem automatitzar el procés utilitzant la següent comanda: | |
+ | <pre> | ||
+ | php bin/console generate:controller | ||
+ | </pre> | ||
+ | Aquesta comanda et demanarà tota la informació necessaria per crear el controlador i els '''actions''' (mètodes) necessaris que podràs utilitzar en el fitxer de rutes. Tant si utilitzes la comanda com si no la utilitzes, sempre podràs canviar la classe generada com tu prefereixis. | ||
− | + | = Encaminament = | |
− | + | Una ruta és una associació entre un patró d'URL i un controlador. Suposem per exemple que desitges associar URL de tipus ''/blog/el meu-post'' o ''/blog/tot-sobre-symfony'' amb un controlador que sigui capaç de buscar i mostrar l'article sol·licitat. | |
− | + | <source lang="yml"> | |
− | + | # app/config/routing.yml | |
− | + | blog_show: | |
− | + | path: /blog/{slug} | |
− | + | defaults: { _controller: AcmeBlogBundle:Blog:show } | |
− | + | </source> | |
+ | Codi PHP del controlador associat a aquesta ruta: | ||
+ | <source lang="php"> | ||
+ | // 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 = // usa la variable $slug para consultar la base de datos | ||
+ | |||
+ | return $this->render('AcmeBlogBundle:Blog:show.html.twig', array( | ||
+ | 'blog' => $blog, | ||
+ | )); | ||
+ | } | ||
+ | } | ||
</source> | </source> | ||
− | Una | + | |
+ | Aquest és l'objectiu del encaminador de Symfony2: associar la URL d'una petició a un controlador | ||
+ | |||
+ | == Funcionament == | ||
+ | L'objectiu del sistema d'encaminament de Symfony2 és analitzar aquesta URL i determinar què controlador s'ha d'executar. El procés complet consta dels següents passos: | ||
+ | *La petició es processa en el controlador frontal de Symfony2 (per exemple, en l'arxiu''' app.php'''). | ||
+ | *El nucli de Symfony2 (conegut com kernel) sol·licita al enrutador que examini la petició. | ||
+ | *El enrutador busca què patró de les rutes de l'aplicació coincideix amb la URL entrant i retorna informació sobre la ruta, incloent el controlador que s'ha d'executar. | ||
+ | *El nucli de Symfony2 executa el controlador, que en última instància, retorna un objecte '''Response'''. | ||
+ | |||
+ | [[Fitxer:funcionament-routing.png]] | ||
+ | |||
+ | == Prefix a les rutes == | ||
+ | |||
+ | Resulta habitual haver d'afegir un prefix a totes les rutes importades des d'un arxiu extern. Si vols per exemple que el patró de la ruta '''acme_hello''' sigui '''/admin/hello/{name}''' en comptes de''' /hello/{name}''', afegeix l'opció prefix en importar les rutes: | ||
+ | <source lang="yml"> | ||
+ | # app/config/routing.yml | ||
+ | acme_hello: | ||
+ | resource: "@AcmeHelloBundle/Resources/config/routing.yml" | ||
+ | prefix: /admin | ||
+ | </source> | ||
+ | El valor indicat en l'opció prefix (en aquest cas '''/admin''') s'afegeix per davant de tots els patrons de les rutes importades des de l'arxiu extern. | ||
+ | |||
+ | = Sessions a Symfony2= | ||
+ | |||
+ | Symfony2 inclou un objecte de sessió que permet emmagatzemar informació persistent sobre l'usuari, és a dir, informació que es guarda d'una petició a una altra. Per defecte Symfony2 emmagatzema la informació en una cookie usant les sessions natives de PHP. | ||
+ | |||
+ | <source lang="php"> | ||
+ | use Symfony\Component\HttpFoundation\Request; | ||
+ | |||
+ | public function indexAction(Request $request) | ||
+ | { | ||
+ | $session = $request->getSession(); | ||
+ | |||
+ | // guarda un atributo para reutilizarlo durante una | ||
+ | // petición posterior del usuario | ||
+ | $session->set('foo', 'bar'); | ||
+ | |||
+ | // obtener el valor de un atributo de la sesión | ||
+ | $foo = $session->get('foo'); | ||
+ | |||
+ | // utilizar un valor por defecto si el atributo no existe | ||
+ | $filters = $session->get('filters', array()); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | =Plantilles TWIG= | ||
+ | Una plantilla és un arxiu de text que pot generar qualsevol altre format basat en text (HTML, XML, CSV, LaTeX, etc.). Les plantilles PHP són les més populars, com la qual mostra el següent exemple: | ||
+ | <source lang="html"> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <title>¡Bienvenido a Symfony!</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <h1><?php echo $page_title ?></h1> | ||
+ | |||
+ | <ul id="navigation"> | ||
+ | <?php foreach ($navigation as $item): ?> | ||
+ | <li> | ||
+ | <a href="<?php echo $item->getHref() ?>"> | ||
+ | <?php echo $item->getCaption() ?> | ||
+ | </a> | ||
+ | </li> | ||
+ | <?php endforeach; ?> | ||
+ | </ul> | ||
+ | </body> | ||
+ | </html> | ||
+ | </source> | ||
+ | Symfony2 inclou un llenguatge de plantilles anomenat '''Twig''' que és molt més potent i elegant que PHP. Gràcies a Twig pots crear plantilles molt concises i fàcils de llegir, per la qual cosa a més són fàcils d'entendre per part dels dissenyadors web. Observa el mateix exemple anterior definit com a plantilla Twig: | ||
+ | <source lang="html"> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <title>¡Bienvenido a Symfony!</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <h1>{{ page_title }}</h1> | ||
+ | |||
+ | <ul id="navigation"> | ||
+ | {% for item in navigation %} | ||
+ | <li><a href="{{ item.href }}">{{ item.caption }}</a></li> | ||
+ | {% endfor %} | ||
+ | </ul> | ||
+ | </body> | ||
+ | </html> | ||
+ | </source> | ||
+ | Twig es basa en dues etiquetes especials: | ||
+ | |||
+ | *'''{ { ... } }''': serveix per mostrar el contingut d'una variable o el resultat de realitzar alguna operació o processar alguna expressió. En PHP la construcció equivalent és ''echo'' o ''print''. | ||
+ | *'''{% ... %}''': serveix per definir la lògica de la plantilla, és a dir, la part de programació que controla com es mostren els continguts de la plantilla. Entre uns altres, aquesta etiqueta s'empra per a les instruccions ''if'' i per als bucles ''for''. | ||
+ | |||
+ | ==Herència de plantilles i layout== | ||
+ | Normalment les plantilles d'un mateix projecte comparteixen molts elements comuns, com per exemple la capçalera, el peu de pàgina, una barra lateral, etc. Symfony2 resol aquest problema de forma molt senzilla: una plantilla pot decorar el contingut d'una altra plantilla. | ||
+ | |||
+ | La idea és exactament la mateixa que l'herència de classes PHP: l'herència de plantilles et permet crear una plantilla basi cridada layout i que conté tots els elements comuns del lloc definits com a blocs. Després, les plantilles filla hereten del layout i emplenen o modifiquen aquests blocs. | ||
+ | |||
+ | En primer lloc, crea un arxiu amb el teu disseny base: | ||
+ | <source lang="html"> | ||
+ | |||
+ | # app/Resources/views/base.html.twig #} | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||
+ | <title>{% block title %}Test Application{% endblock %}</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <div id="sidebar"> | ||
+ | {% block sidebar %} | ||
+ | <ul> | ||
+ | <li><a href="/">Home</a></li> | ||
+ | <li><a href="/blog">Blog</a></li> | ||
+ | </ul> | ||
+ | {% endblock %} | ||
+ | </div> | ||
+ | |||
+ | <div id="contenido"> | ||
+ | {% block body %}{% endblock %} | ||
+ | </div> | ||
+ | </body> | ||
+ | </html> | ||
+ | |||
+ | </source> | ||
+ | Aquesta plantilla defineix l'esquelet d'una pàgina HTML simple de dues columnes. En aquest exemple, '''es defineixen tres blocs amb l'etiqueta {% block %} (title, sidebar i body)'''. Les '''plantilles filla''' poden '''modificar''' els continguts de cadascun dels '''blocs'' o deixar-los tal com estan en la plantilla base. | ||
+ | |||
+ | El següent exemple mostra l'aspecte d'una plantilla filla: | ||
+ | <source lang="php"> | ||
+ | {# 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 %} | ||
+ | <h2>{{ entry.title }}</h2> | ||
+ | <p>{{ entry.body }}</p> | ||
+ | {% endfor %} | ||
+ | {% endblock %} | ||
+ | |||
+ | </source> | ||
+ | |||
+ | La clau de l'herència de plantilles és l'etiqueta '''{% extends %}'''. D'aquesta forma el motor de plantilles sap que primer ha de processar la plantilla base, que defineix el disseny de la pàgina i crea diversos blocs de continguts. Després es renderitza la plantilla filla, que reemplaça el contingut dels blocs '''title''' i '''body''' del pare. | ||
+ | |||
+ | Quan treballis amb l'herència de plantilles, tingues en compte: | ||
+ | *Si inclous l'etiqueta '''{% extends %}''' en una plantilla, aquesta ha de ser la primera etiqueta d'aquesta plantilla. | ||
+ | *Quantes més etiquetes '''{% block %}''' tinguis en la teva plantilla basi, millor. Recorda que les plantilles filla no tenen l'obligació d'emplenar tots els blocs dels pares, per la qual cosa pots definir tants blocs com vulguis i assignar a cadascun un valor per defecte que sigui lògic per a la major part de les pàgines del lloc. Quants més blocs defineixi el layout, més flexible serà el seu disseny. | ||
+ | *Si còpies i pegues algun contingut en diverses plantilles, segurament serà millor que moguis aquest contingut a algun bloc del layout. En altres casos el millor és col·locar aquest contingut en alguna altra plantilla i incloure-la amb l'etiqueta '''include''' sempre que sigui necessari. | ||
+ | *Si vols obtenir el contingut d'algun bloc de la plantilla pare, utilitza la funció''' { { parent() } }'''. Això és molt útil quan vols afegir continguts propis a qualsevol altre contingut que el pare pugui haver definit per a aquest bloc: | ||
+ | <source lang="php"> | ||
+ | {% block sidebar %} | ||
+ | <h3>Table of Contents</h3> | ||
+ | |||
+ | {# ... #} | ||
+ | |||
+ | {{ parent() }} | ||
+ | {% endblock %} | ||
+ | </source> | ||
+ | |||
+ | ==Localització de les plantilles== | ||
+ | Les plantilles es troben en dos llocs diferents: | ||
+ | |||
+ | *'''app/Resources/views/''': el directori on s'emmagatzemen les plantilles globals de l'aplicació, com per exemple la plantilla del layout i les plantilles utilitzades per redefinir les plantilles incloses en els bundles de Symfony2. | ||
+ | *'''ruta/fins a/el/bundle/Resources/views/''': els bundles emmagatzemen les seves pròpies plantilles en el directori '''Resources/views/''' de cada bundle. La immensa majoria de les plantilles s'emmagatzemen dins d'algun bundle. | ||
+ | Symfony2 identifica a cada plantilla amb la sintaxi especial '''bundle:controlador:plantilla'''. Això permet definir diferents tipus de plantilla, cadascuna emmagatzemada en un lloc diferent: | ||
+ | |||
+ | '''AcmeBlogBundle:Blog:index.html.twig''': indica la plantilla d'una pàgina específica. Est és el significat de cadascuna de les tres parts de la cadena: | ||
+ | *'''AcmeBlogBundle''': (bundle) la plantilla es troba dins del bundle '''AcmeBlogBundle''' (per exemple, ''src/Acme/BlogBundle''). | ||
+ | *'''Blog''': (controlador) indica que la plantilla s'emmagatzema en el subdirectori Blog de '''Resources/views'''. | ||
+ | *'''index.html.twig''': (plantilla) el nom de l'arxiu que guarda la plantilla és '''index.html.twig'''. Suposant que '''AcmeBlogBundle''' es trobi en '''src/Acme/BlogBundle''', la ruta completa de la plantilla seria '''src/Acme/BlogBundle/Resources/views/Blog/index.html.twig'''. | ||
+ | |||
+ | *'''AcmeBlogBundle::layout.html.twig''': indica que és una plantilla global del bundle indicat. Com a falta la part central que es refereix al controlador, aquesta plantilla '''no es troba''' dins de cap subdirectori de '''Resources/views/'''. En altres paraules, '''la ruta '''completa de la plantilla és '''Resources/views/layout.html.twig''' dins del bundle AcmeBlogBundle. | ||
+ | *'''::base.html.twig''': indica que és una plantilla global de l'aplicació. Observa que la cadena comença amb dos parells de dos punts '''(::)''', per la qual cosa falta la part del bundle i la del controlador. En altres paraules, aquesta plantilla no es troba en cap bundle sinó directament dins de '''app/Resources/views/'''. | ||
+ | |||
+ | == Etiquetes Twig== | ||
+ | ===Incloent unes altres plantilles=== | ||
+ | Resulta habitual voler incloure la mateixa plantilla o fragment de codi a diverses pàgines diferents. Si l'aplicació té per exemple un llistat d'articles, el codi de la plantilla que mostra un article es pot utilitzar a la pàgina de detall de l'article, en una pàgina que mostra els articles més populars, o en una llista dels articles més recents. | ||
+ | |||
+ | En PHP, quan necessites reutilitzar un tros de codi, normalment mous el codi a una nova classe o funció. En les plantilles s'aplica la mateixa idea: | ||
+ | <source lang="html"> | ||
+ | {# src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.twig #} | ||
+ | <h2>{{ article.title }}</h2> | ||
+ | <h3 class="byline">by {{ article.authorName }}</h3> | ||
+ | |||
+ | <p> | ||
+ | {{ article.body }} | ||
+ | </p> | ||
+ | </source> | ||
+ | Ara ja pots incloure fàcilment aquesta plantilla en qualsevol una altra: | ||
+ | |||
+ | <source lang="html"> | ||
+ | {# src/Acme/ArticleBundle/Resources/views/Article/list.html.twig #} | ||
+ | {% extends 'AcmeArticleBundle::layout.html.twig' %} | ||
+ | |||
+ | {% block body %} | ||
+ | <h1>Recent Articles<h1> | ||
+ | |||
+ | {% for article in articles %} | ||
+ | {{ include( | ||
+ | 'AcmeArticleBundle:Article:articleDetails.html.twig', | ||
+ | {'article': article} | ||
+ | ) }} | ||
+ | {% endfor %} | ||
+ | {% endblock %} | ||
+ | </source> | ||
+ | |||
+ | ==Renderitzant plantilles enviant-li dades des de el controlador== | ||
+ | Imagina que en el teu lloc web tens una barra lateral que mostra els tres articles més recents. Per obtenir aquests tres articles és necessari realitzar una consulta a la base de dades o alguna altra operació similar que no es pot incloure en la pròpia plantilla. | ||
+ | |||
+ | La solució consisteix a inserir en la plantilla el resultat retornat per un controlador de l'aplicació. En primer lloc, crea un controlador que renderitzi el llistat dels articles recents: | ||
+ | <source lang="php"> | ||
+ | // src/Acme/ArticleBundle/Controller/ArticleController.php | ||
+ | class ArticleController extends Controller | ||
+ | { | ||
+ | public function recentArticlesAction($max = 3) | ||
+ | { | ||
+ | // hace una llamada a la base de datos u otra lógica | ||
+ | // para obtener los "$max" artículos más recientes | ||
+ | $articles = ...; | ||
+ | |||
+ | return $this->render( | ||
+ | 'AcmeArticleBundle:Article:recentList.html.twig', | ||
+ | array('articles' => $articles) | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | i a la plantilla es pot utilitzar les dades del array enviat des del controlador directament: | ||
+ | <source lang="html"> | ||
+ | {# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #} | ||
+ | {% for article in articles %} | ||
+ | <a href="/article/{{ article.slug }}"> | ||
+ | {{ article.title }} | ||
+ | </a> | ||
+ | {% endfor %} | ||
+ | </source> | ||
+ | |||
+ | Per mostrar en qualsevol plantilla el resultat d'executar un controlador, utilitza la funció render i identifica al controlador utilitzant la notació especial '''bundle:controlador:acció''': | ||
+ | <source lang="html"> | ||
+ | {# app/Resources/views/base.html.twig #} | ||
+ | |||
+ | {# ... #} | ||
+ | <div id="sidebar"> | ||
+ | {{ render(controller('AcmeArticleBundle:Article:recentArticles', { | ||
+ | 'max': 3 | ||
+ | })) }} | ||
+ | </div> | ||
+ | </source> | ||
+ | |||
+ | Exemple de IF dintre del twig: | ||
+ | |||
+ | <source lang="html"> | ||
+ | |||
+ | {% if kenny.sick %} | ||
+ | Kenny is sick. | ||
+ | {% elseif kenny.dead %} | ||
+ | You killed Kenny! You bastard!!! | ||
+ | {% else %} | ||
+ | Kenny looks okay --- so far | ||
+ | {% endif %} | ||
+ | |||
+ | </source> | ||
+ | |||
+ | ==Enllaçant pàgines== | ||
+ | Crear enllaços a altres pàgines de l'aplicació és una de les tasques més comunes d'una plantilla. En lloc de generar a mà les URL dins de la plantilla, utilitza la funció path de Twig (o el helper router en PHP) per generar les URL utilitzant la configuració del sistema de encaminament. D'aquesta manera, si més endavant vols canviar l'aspecte de qualsevol URL, només has d'actualitzar un arxiu de configuració i totes les URL de les plantilles s'actualitzaran instantàniament. | ||
+ | <source lang="javascript"> | ||
+ | <a href="{{ path('_welcome') }}">Home</a> | ||
+ | <a href="{{ path('hello',{ "id": '2'}) }}">Home</a> | ||
+ | </source> | ||
+ | On '''_welcome''' està definit al fitxer de routes: | ||
+ | <source lang="html"> | ||
+ | _welcome: | ||
+ | path: / | ||
+ | defaults: { _controller: AcmeDemoBundle:Welcome:index } | ||
+ | </source> | ||
+ | |||
+ | ==Enllaçant arxius web (css, javascript)== | ||
+ | Les plantilles de les aplicacions web solen enllaçar amb els web '''assets'' o arxius web, tals com a imatges, fulles d'estil CSS, arxius Javascript, etc. | ||
+ | |||
+ | A la versió 2.8 ja no s'instal·la '''assetic''' per defecte i s'ha d'instal·lar utilitzant aquesta guia: | ||
+ | http://symfony.com/doc/current/cookbook/assetic/asset_management.html | ||
+ | |||
+ | '''Quan passem a producció hem d'executar:''' | ||
+ | sudo php app/console assetic:dump --env=prod --no-debug | ||
+ | |||
+ | |||
+ | De nou no és aconsellable generar a mà les URL d'aquest tipus d'arxius, ja que Symfony2 ofereix una solució millor i molt més flexible mitjançant la funció '''asset''' de Twig: | ||
+ | <source lang="html"> | ||
+ | <img src="{{ asset('images/logo.png') }}" alt="Symfony!" /> | ||
+ | |||
+ | <link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" /> | ||
+ | </source> | ||
+ | |||
+ | La gestió dels arxius CSS i dels arxius Javascript es realitza mitjançant la herència de plantilles. A la plantilla para es pot incloure els arxius que s'utilitzarà a tota la aplicació i després, a les plantilles filles, es poden incloure fitxers específics per aquella part de la web: | ||
+ | Plantilla pare: | ||
+ | <source lang="html"> | ||
+ | {# app/Resources/views/base.html.twig #} | ||
+ | <html> | ||
+ | <head> | ||
+ | {# ... #} | ||
+ | |||
+ | {% block stylesheets %} | ||
+ | <link href="{{ asset('css/main.css') }}" type="text/css" rel="stylesheet" /> | ||
+ | {% endblock %} | ||
+ | </head> | ||
+ | <body> | ||
+ | {# ... #} | ||
+ | |||
+ | {% block javascripts %} | ||
+ | <script src="{{ asset('js/main.js') }}" type="text/javascript"></script> | ||
+ | {% endblock %} | ||
+ | </body> | ||
+ | </html> | ||
+ | |||
+ | </source> | ||
+ | i les plantilles filles redefineixen els blocs tot utilitzant la configuració que hi ha en la plantilla pare: | ||
+ | <source lang="html"> | ||
+ | {# src/Acme/DemoBundle/Resources/views/Contact/contact.html.twig #} | ||
+ | {% extends '::base.html.twig' %} | ||
+ | |||
+ | {% block stylesheets %} | ||
+ | {{ parent() }} | ||
+ | |||
+ | <link href="{{ asset('css/contact.css') }}" type="text/css" rel="stylesheet" /> | ||
+ | {% endblock %} | ||
+ | |||
+ | {# ... #} | ||
+ | </source> | ||
+ | Les plantilles també poden enllaçar arxius que es trobin en el directori Resources/public de qualsevol bundle. Per fer que aquests arxius estiguin disponibles en l'aplicació, executa la comanda: | ||
<pre> | <pre> | ||
− | + | php app/console assets:install directorio [--symlink] | |
− | |||
− | |||
− | |||
− | |||
− | |||
</pre> | </pre> | ||
+ | |||
+ | ===Si al utilitzar ''assets'' en producció no funciona:=== | ||
+ | http://symfony.com/doc/current/cookbook/assetic/asset_management.html#cookbook-assetic-dumping | ||
+ | |||
+ | == Variables globals que es poden utilitzar directament a les plantilles == | ||
+ | *'''app.security''' - el context de seguretat. | ||
+ | *'''app.user''' - l'objecte qeu representa a l'usuari que està visitant l'aplicació. | ||
+ | *'''app.request''' - l'objecte Request que conté tota la informació de la petició. | ||
+ | *'''app.session''' - l'objecte Session relacionat amb l'usuari. | ||
+ | *'''app.environment''' - l'entorn en el qual s'està executant l'aplicació (dev, prod, etc.) | ||
+ | *'''app.debug''' - val true si l'aplicació s'està executant en la manera de depuració i false en un altre cas. | ||
+ | =Formularis= | ||
+ | Guia Symfony per crear i utilitzar formularis: | ||
+ | http://symfony.com/doc/2.8/book/forms.html | ||
+ | |||
+ | Symfony incorpora unes llibreries que permeten utilitzar els formularis associant-los amb clases PHP. Exemple: | ||
+ | Classe Tasca: | ||
+ | <source lang="php"> | ||
+ | // src/Acme/TaskBundle/Entity/Task.php | ||
+ | namespace Acme\TaskBundle\Entity; | ||
+ | |||
+ | class Task | ||
+ | { | ||
+ | // descripción de la tarea | ||
+ | protected $task; | ||
+ | |||
+ | // fecha en la que debe estar completada | ||
+ | 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; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | Per crear el formulari on demanem totes, o algunes, de les dades que necessitem per crear la tasca anterior fariem el següent: | ||
+ | <source lang="php"> | ||
+ | // 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) | ||
+ | { | ||
+ | // crea una task y le asigna algunos datos ficticios para este ejemplo | ||
+ | $task = new Task(); | ||
+ | $task->setTask('Write a blog post'); | ||
+ | $task->setDueDate(new \DateTime('tomorrow')); | ||
+ | |||
+ | $form = $this->createFormBuilder($task) | ||
+ | ->add('task', 'text') | ||
+ | ->add('dueDate', 'date') | ||
+ | ->add('save', 'submit') | ||
+ | ->getForm(); | ||
+ | |||
+ | return $this->render('AcmeTaskBundle:Default:new.html.twig', array( | ||
+ | 'formulari' => $form->createView(), | ||
+ | )); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Utilitzant el formulari en una plantilla TWIG: | ||
+ | <source lang="html"> | ||
+ | {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} | ||
+ | |||
+ | {{ form(formulari) }} | ||
+ | </source> | ||
+ | ==Processant l'enviament del formulari== | ||
+ | Exemple del controlador que envia i reb les dades d'un formulari: | ||
+ | <source lang="php"> | ||
+ | public function newAction(Request $request) | ||
+ | { | ||
+ | // crear un objeto $task nuevo (borra los datos de prueba) | ||
+ | $task = new Task(); | ||
+ | |||
+ | $form = $this->createFormBuilder($task) | ||
+ | ->setAction($this->generateUrl('target_route')) | ||
+ | ->setMethod('GET') | ||
+ | ->add('task', 'text') | ||
+ | ->add('dueDate', 'date') | ||
+ | ->add('save', 'submit') | ||
+ | ->getForm(); | ||
+ | |||
+ | $form->handleRequest($request); | ||
+ | |||
+ | if ($form->isValid()) { | ||
+ | // guardar la tarea en la base de datos | ||
+ | |||
+ | return $this->redirect($this->generateUrl('task_success')); | ||
+ | } | ||
+ | |||
+ | // ... | ||
+ | } | ||
+ | </source> | ||
+ | *Quan es carrega per primera vegada la pàgina associada a aquest controlador, es crea i renderiza el formulari. El mètode '''handleRequest() ''' detecta que el formulari no s'ha enviat i per tant, no fa gens.El mètode '''isValid()''' retorna false si el formulari no s'ha enviat. | ||
+ | *Quan l'usuari envia el formulari, el mètode '''handleRequest()''' ho detecta i guarda immediatament les dades enviades en les propietats '''task i dueDate''' de l'objecte''' $task.''' Després es valida aquest objecte. Si no és vàlid, el mètode '''isValid()''' retorna '''false''' una altra vegada, per la qual cosa es torna a mostrar el formulari, aquesta vegada amb els missatges d'error corresponents. Si solament vols comprovar si el formulari s'ha enviat, independentment de si és vàlid o no, utilitza el mètode '''isSubmitted()'''. | ||
+ | *Quan l'usuari envia el formulari amb dades vàlides, les dades enviades es guarden de nou en el formulari, però aquesta vegada el mètode '''isValid()''' retorna '''true'''. En aquest moment ja pots treballar amb l'objecte '''$task''' (per exemple guardant-ho en una base de dades) abans de redirigir a l'usuari a una altra pàgina (per exemple a la pàgina d'agraïment o a la qual mostra un missatge determinat). | ||
+ | |||
+ | =Seguretat= | ||
+ | És un procés de dues etapes que el seu objectiu és evitar que un usuari accedeixi a un recurs pel qual no hauria de tenir accés. | ||
+ | |||
+ | En el primer pas del procés, el sistema de seguretat identifica qui és l'usuari obligant-ho a enviar algun tipus d'identificació. Això es diu autenticació, i significa que el sistema està tractant d'esbrinar qui ets. | ||
+ | |||
+ | Una vegada que el sistema sap qui ets, el següent pas és decidir si hauries de tenir accés a un determinat recurs. Aquesta part del procés es diu autorització, i significa que el sistema està comprovant si tens suficients privilegis per realitzar una determinada acció. | ||
+ | |||
+ | [[Fitxer:seguretat-symfony.png]] | ||
+ | |||
+ | ==Firewalls (autenticació)== | ||
+ | |||
+ | El sistema de seguretat de Symfony s'activa quan un usuari fa una petició a una URL que està protegida per un '''firewall''' o tallafocs. El treball del firewall ''consisteix a determinar si l'usuari necessita estar autenticat'', i si ho necessita, enviar una resposta a l'usuari per iniciar el procés d'autenticació. | ||
+ | |||
+ | Un ''firewall s'activa quan la URL d'una petició entrant concorda amb el valor de la seva opció de configuració '''pattern'''''. En aquest exemple el valor de pattern''' (^/)''' concorda amb qualsevol petició entrant. No obstant això, el fet que el firewall estigui activat no significa que el navegador mostra la caixa de login+contrasenya per a totes les URL. Els usuaris poden accedir per exemple a /foo sense que l'aplicació els demani que s'autentiquin. | ||
+ | |||
+ | [[Fitxer:firewall-symfony.png]] | ||
+ | |||
+ | Exemple: | ||
+ | <source lang="html"> | ||
+ | # app/config/security.yml | ||
+ | security: | ||
+ | firewalls: | ||
+ | secured_area: | ||
+ | pattern: ^/ | ||
+ | anonymous: ~ | ||
+ | http_basic: | ||
+ | realm: "Secured Demo Area" | ||
+ | |||
+ | access_control: | ||
+ | - { path: ^/admin, roles: ROLE_ADMIN } | ||
+ | # Descomenta la siguiente línea para proteger también | ||
+ | # la propia URL /admin | ||
+ | # - { path: ^/admin$, roles: ROLE_ADMIN } | ||
+ | |||
+ | providers: | ||
+ | in_memory: | ||
+ | memory: | ||
+ | users: | ||
+ | ryan: { password: ryanpass, roles: 'ROLE_USER' } | ||
+ | admin: { password: kitten, roles: 'ROLE_ADMIN' } | ||
+ | |||
+ | encoders: | ||
+ | Symfony\Component\Security\Core\User\User: plaintext | ||
+ | </source> | ||
+ | |||
+ | Aquest funcionament és possible en primer lloc perquè el firewall permet l'accés als usuaris anònims a causa de l'opció de configuració anonymous. En altres paraules, el firewall no exigeix que tots els usuaris s'autentiquin res més accedir a l'aplicació. I com en la configuració de la secció access_control no s'indica que els usuaris hagin de tenir cap role especial per accedir a /foo la petició es processa sense requerir a l'usuari que s'autentiqui. | ||
+ | |||
+ | Si elimines l'opció anonymous, l'efecte és que ara el firewall demana autenticació a qualsevol recurs. | ||
+ | |||
+ | Seguint amb el mateix exemple, si un usuari sol·licita '''/admin/foo''', l'aplicació es comporta de manera diferent. Això és a causa de la configuració de la secció '''access_control''', que indica que qualsevol URL que coincideixi amb l'expressió regular '''^/admin''' (és a dir, la URL '''/admin''' o qualsevol altra URL que coincideixi amb /admin/) requereix el rol''' ROLE_ADMIN'''. Els rols són la clau del sistema d'autorització: l'usuari pot accedir a '''/admin/foo''' només si compta amb el rol'''ROLE_ADMIN'''. | ||
+ | |||
+ | [[Fitxer:access-symfony.png ]] | ||
+ | |||
+ | La capa de control d'accés denega l'accés a l'usuari (perquè els usuaris anònims no compten amb el rol ROLE_ADMIN), el firewall pren el control de l'aplicació i inicia el procés d'autenticació. | ||
+ | |||
+ | ==Utilitzant el típic formulari de accés== | ||
+ | En primer lloc, afegeix l'opció form_login en la configuració del firewall: | ||
+ | <source lang="html"> | ||
+ | # app/config/security.yml | ||
+ | security: | ||
+ | firewalls: | ||
+ | secured_area: | ||
+ | pattern: ^/ | ||
+ | anonymous: ~ | ||
+ | form_login: | ||
+ | login_path: login | ||
+ | check_path: login_check | ||
+ | </source> | ||
+ | |||
+ | El sistema de seguretat inicia el procés d'autenticació, es redirigeix a l'usuari a la ruta que mostra el formulari d'accés (per defecte '''/login'''). El'' formulari has de crear-ho tu mateix a mà, ja que Symfony no ho proporciona''. Primer crea les dues noves rutes utilitzades en la configuració de la seguretat: la ruta '''login''' mostra el formulari (es correspon amb la URL''' /login''') i '''login_check''' que processa l'enviament del formulari (es correspon amb la URL '''/login_check)''': | ||
+ | |||
+ | <source lang="html"> | ||
+ | # app/config/routing.yml | ||
+ | login: | ||
+ | pattern: /login | ||
+ | defaults: { _controller: AcmeSecurityBundle:Security:login } | ||
+ | login_check: | ||
+ | pattern: /login_check | ||
+ | </source> | ||
+ | |||
+ | Observa que el nom de la ruta '''login''' coincideix amb el valor de l'opció '''login_path''', ja que és on el sistema de seguretat redirigeix als usuaris que necessiten autenticar-se. | ||
+ | |||
+ | A continuació, crea el controlador que mostra el formulari de accés: | ||
+ | <source lang="php"> | ||
+ | // src/Acme/SecurityBundle/Controller/SecurityController.php; | ||
+ | namespace Acme\SecurityBundle\Controller; | ||
+ | |||
+ | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
+ | use Symfony\Component\HttpFoundation\Request; | ||
+ | use Symfony\Component\Security\Core\SecurityContext; | ||
+ | |||
+ | class SecurityController extends Controller | ||
+ | { | ||
+ | public function loginAction(Request $request) | ||
+ | { | ||
+ | $session = $request->getSession(); | ||
+ | |||
+ | // get the login error if there is one | ||
+ | if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { | ||
+ | $error = $request->attributes->get( | ||
+ | SecurityContext::AUTHENTICATION_ERROR | ||
+ | ); | ||
+ | } else { | ||
+ | $error = $session->get(SecurityContext::AUTHENTICATION_ERROR); | ||
+ | $session->remove(SecurityContext::AUTHENTICATION_ERROR); | ||
+ | } | ||
+ | |||
+ | return $this->render( | ||
+ | 'AcmeSecurityBundle:Security:login.html.twig', | ||
+ | array( | ||
+ | // last username entered by the user | ||
+ | 'last_username' => $session->get(SecurityContext::LAST_USERNAME), | ||
+ | 'error' => $error, | ||
+ | ) | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | Si l'usuari envia un nom d'usuari o contrasenya no vàlids, aquest controlador obté el missatge d'error del sistema de seguretat i ho mostra a l'usuari. | ||
+ | Tu t'encarregues de mostrar el formulari a l'usuari i els errors que puguin haver ocorregut, però ''el propi sistema de seguretat s'encarrega de verificar el nom d'usuari i contrasenya i l'autenticació de l'usuari''. | ||
+ | Plantilla del formulari: | ||
+ | <source lang="html"> | ||
+ | {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} | ||
+ | {% if error %} | ||
+ | <div>{{ error.message }}</div> | ||
+ | {% endif %} | ||
+ | |||
+ | <form action="{{ path('login_check') }}" method="post"> | ||
+ | <label for="username">Username:</label> | ||
+ | <input type="text" id="username" name="_username" value="{{ last_username }}" /> | ||
+ | |||
+ | <label for="password">Password:</label> | ||
+ | <input type="password" id="password" name="_password" /> | ||
+ | |||
+ | {# | ||
+ | añade lo siguiente si quieres redirigir al usuario a una | ||
+ | URL concreta después del login (explicado más adelante) | ||
+ | <input type="hidden" name="_target_path" value="/account" /> | ||
+ | #} | ||
+ | |||
+ | <button type="submit">login</button> | ||
+ | </form> | ||
+ | </source> | ||
+ | =Webgrafia= | ||
+ | Els llibres sobre symfony utilitzats per elaborar un resum de les funcionalitats més importants els podeu trobar a : [http://librosweb.es/libros/ http://librosweb.es/libros/] | ||
+ | |||
+ | També s'ha utilitzat el llibre oficial que podeu trobar a la web:[http://symfony.com/doc/current/book/index.html http://symfony.com/] |
Revisió de 02:24, 28 feb 2018
Contingut
- 1 Introducció a Symfony
- 2 Característiques
- 3 Desenvolupament ràpid d'aplicacions (RAD)
- 4 La implementació del MVC que realitza Symfony
- 5 Instal·lació de Symfony
- 6 Estructura del projecte: Aplicacions, Mòduls i Accions
- 7 Estructura de l'arbre de arxius
- 8 Els Bundles
- 9 Entorn de desenvolupament i entorn de producció
- 10 El controlador
- 11 Encaminament
- 12 Sessions a Symfony2
- 13 Plantilles TWIG
- 13.1 Herència de plantilles i layout
- 13.2 Localització de les plantilles
- 13.3 Etiquetes Twig
- 13.4 Renderitzant plantilles enviant-li dades des de el controlador
- 13.5 Enllaçant pàgines
- 13.6 Enllaçant arxius web (css, javascript)
- 13.7 Variables globals que es poden utilitzar directament a les plantilles
- 14 Formularis
- 15 Seguretat
- 16 Webgrafia
Introducció a Symfony
Un framework simplifica el desenvolupament de les aplicacions, ja que automatitza molts dels patrons utilitzats per resoldre les tasques comunes. A més, un framework proporciona estructura al codi font, forçant al desenvolupador a crear codi més llegible i més fàcil de mantenir. Finalment, un framework facilita la programació d'aplicacions, ja que encapsula operacions complexes en instruccions senzilles.
Symfony és un complet framework dissenyat per optimitzar, gràcies a les seves característiques, el desenvolupament de les aplicacions web. Per començar, separa la lògica de negoci, la lògica de servidor i la presentació de l'aplicació web. Proporciona diverses eines i classes encaminades a reduir el temps de desenvolupament d'una aplicació web complexa. A més, automatitza les tasques més comunes, permetent al desenvolupador dedicar-se per complet als aspectes específics de cada aplicació. El resultat de tots aquests avantatges és que no s'ha de reinventar la roda cada vegada que es crea una nova aplicació web.
Symfony està desenvolupat completament amb PHP i ha estat provat amb èxit en llocs com Yahoo! Answers, delicious, DailyMotion i molts altres llocs web de primer nivell. Symfony és compatible amb la majoria de gestors de bases de dades, com MySQL, PostgreSQL, Oracle i SQL Server de Microsoft. Es pot executar tant en plataformes Unix (Unix, Linux, etc.) com en plataformes Windows. A continuació es mostren algunes de les seves característiques.
Característiques
Symfony es va dissenyar perquè s'ajustés als següents requisits:
- Fàcil d'instal·lar i configurar en la majoria de plataformes (i amb la garantia que funciona correctament en els sistemes Windows i *nix estàndards)
- Independent del sistema gestor de bases de dades
- Senzill d'usar en la majoria de casos, però prou flexible com per adaptar-se als casos més complexos
- Basat en la premissa de "convenir en comptes de configurar", en la qual el desenvolupador solament ha de configurar allò que no és convencional
- Segueix la majoria de millors pràctiques i patrons de disseny per a la web
- Preparat per a aplicacions empresarials i adaptable a les polítiques i arquitectures pròpies de cada empresa, a més de ser prou estable com per desenvolupar aplicacions a llarg termini
- Codi fàcil de llegir que inclou comentaris de phpDocumentor i que permet un manteniment molt senzill
- Fàcil d'estendre, la qual cosa permet la seva integració amb llibreries desenvolupades per tercers
Symfony pot ser completament personalitzat per complir amb els requisits de les empreses que disposen de les seves pròpies polítiques i regles per a la gestió de projectes i la programació d'aplicacions. Per defecte incorpora diversos entorns de desenvolupament diferents i inclou diverses eines que permeten automatitzar les tasques més comunes de l'enginyeria del programari:
Les eines que generen automàticament codi han estat dissenyades per fer prototips d'aplicacions i per crear fàcilment la part de gestió de les aplicacions.
- El framework de desenvolupament de proves unitàries i funcionals proporciona les eines ideals per al desenvolupament basat en proves "test-driven development").
- La barra de depuració web simplifica la depuració de les aplicacions, ja que mostra tota la informació que els programadors necessiten sobre la pàgina en la qual estan treballant.
- La interfície de línia de comandos automatitza la instal·lació de les aplicacions entre servidors.
- És possible realitzar canvis "en calent" de la configuració (sense necessitat de reiniciar el servidor).
- El complet sistema de log permet als administradors accedir fins a l'últim detall de les activitats que realitza l'aplicació.
Desenvolupament ràpid d'aplicacions (RAD)
Durant molt temps, la programació d'aplicacions web va ser un tasca tediosa i molt lenta. Seguint els cicles habituals de l'enginyeria del programari (com els proposats pel Procés Racional Unificat o Rational Unified Process) el desenvolupament d'una aplicació web no pot començar fins que s'han establert per escrit una sèrie de requisits, s'han creat els diagrames UML Unified Modeling Language) i s'ha produït abundant documentació sobre el projecte. Aquest model es veia afavorit per la baixa velocitat de desenvolupament, la falta de versatilitat dels llenguatges de programació (abans d'executar el programa s'ha de construir, compilar i reiniciar) i sobretot pel fet que els clients no estaven disposats a adaptar-se a altres metodologies.
Avui dia, les empreses reaccionen més ràpidament i els clients canvien d'opinió constantment durant el desenvolupament dels projectes. D'aquesta manera, els equips de desenvolupament han d'adaptar-se a aquestes necessitats i han de poder canviar l'estructura d'una aplicació de forma ràpida. Afortunadament, l'ús de llenguatges de script com Python, Ruby i PHP permeten seguir altres estratègies de programació, com RAD (desenvolupament ràpid d'aplicacions) i el desenvolupament àgil de programari.
Una de les idees centrals d'aquesta metodologia és que el desenvolupament comença al més aviat possible perquè el client pugui revisar un prototip que funciona i pugui indicar el camí a seguir. A partir d'aquí, l'aplicació es desenvolupa de forma iterativa, en la qual cada nova versió incorpora noves funcionalitats i es desenvolupa en un breu espai de temps.
Les conseqüències d'aquestes metodologies per al desenvolupador són nombroses. El programador no ha de pensar sobre les versions futures en incloure una nova funcionalitat. Els mètodes utilitzats han de ser el més senzills i directes possibles. Aquestes idees es resumeixen en el principi denominat KISS: Fes-ho senzill, idiota!Keep It Simple, Stupid
Quan es modifiquen els requisits o quan s'afegeix una nova funcionalitat, normalment s'ha de reescriure part del codi existent. Aquest procés es diu refactorización i succeeix sovint durant el desenvolupament d'una aplicació web. El codi sol moure's a altres llocs en funció de la seva naturalesa. Els blocs de codi repetits es refactorizan en un únic lloc, aplicant el principi DRY: No et repeteixis Don't Repeat Yourself.
Per assegurar que l'aplicació segueix funcionant correctament malgrat els canvis constants, es necessita una sèrie de proves unitàries que puguin ser automatitzades. Si estan ben escrites, les proves unitàries permeten assegurar que gens ha deixat de funcionar després d'haver-hi refactorizado part del codi de l'aplicació. Algunes metodologies de desenvolupament d'aplicacions obliguen a escriure les proves abans que el propi codi, la qual cosa es coneix com TDD: desenvolupament basat en proves test-driven development.
Symfony és l'eina ideal pel RAD. De fet, el framework ha estat desenvolupat per una empresa que aplica el RAD als seus propis projectes. Per aquest motiu, aprendre a utilitzar Symfony no és com aprendre un nou llenguatge de programació, sinó que consite a aprendre a prendre les decisions correctes per desenvolupar les aplicacions de forma més efectiva.
La implementació del MVC que realitza Symfony
- La capa del Modelo
- Abstracció de la base de dades
- Accés a les dades
- La capa de la Vista
- Vista
- Plantilla
- Layout
- La capa del Controlador
- Controlador frontal
- Acció
En total són set scripts, la qual cosa semblen molts arxius per obrir i modificar cada vegada que es crea una pàgina. Afortunadament, Symfony simplifica aquest procés. Symfony pren el millor de l'arquitectura MVC i la implementa de manera que el desenvolupament d'aplicacions sigui ràpid i senzill.
En primer lloc, el controlador frontal i el layout són comuns per a totes les accions de l'aplicació. Es poden tenir diversos controladors i diversos layouts, però solament és obligatori tenir un de cada. El controlador frontal és un component que només té codi relatiu al MVC, per la qual cosa no és necessari crear un, ja que Symfony ho genera de forma automàtica.
L'altra bona notícia és que les classes de la capa del model també es generen automàticament, en funció de l'estructura de dades de l'aplicació. El ORM s'encarrega de crear l'esquelet o estructura bàsica de les classes i genera automàticament tot el codi necessari. Quan el ORM troba restriccions de claus foranes (o externes) o quan troba dades de tipus data, crea mètodes especials per accedir i modificar aquestes dades, per la qual cosa la manipulació de dades es converteix en un joc de nens. L'abstracció de la base de dades és completament transparent per al programador, ja que es realitza de forma nativa mitjançant PDO PHP Data Objects). Així, si es canvia el sistema gestor de bases de dades a qualsevol moment, no s'ha de reescriure ni una línia de codi, ja que tan sols és necessari modificar un paràmetre en un arxiu de configuració.
Finalment, la lògica de la vista es pot transformar en un arxiu de configuració senzill, sense necessitat de programar-la.
Instal·lació de Symfony
Utilitzarem la versió de symfony 2.4.
Instal·lació de la versió 2.4 de symfony (antic)
Instal·lació de la versió LTS 2.8 de symfony
Accedeix a la URL: http://localhost/m7/web/app.php
nota: m7 és, en aquest cas, el nomProjecteSymfony utilitzat en la creació del projecte symfony.
Hauries de veure una pàgina com aquesta:
Creació de la primera pàgina:
Una vegada executis la línia següent anirà preguntant opcions. Tot per defecte. Al nom del bundle podeu posar: HolaMonBundle
php app/console generate:bundle --namespace=m7/HolaMon --format=yml
Iniciar el servidor
Symfony pot ser instal·lat dintre d'un servidor apache2 però per entorns de desenvolupament porta un servidor web integrat. Per executar el servidor i poder provar les aplicacions podeu iniciar-lo amb la comanda de terminal:
php app/console server:run
Si heu engegat el servidor integrat podeu provar la pàgina creada en el punt anterior utilitzant aquesta url
Si teniu apache2 instal·lat, podeu provar la pàgina utilitzant aquesta url_apache_m7
Si hi ha un error accedint a la pàgina!!
Amb apache2, si en algun moment deixa de funcionar heu de provar de fer:
sudo php app/console cache:clear --env=prod && sudo php app/console cache:warmup --env=prod
i donar-li permisos (una altre vegada) a la carpeta principal (dintre de /var/www/html):
chmod 777 nomprojecte/ -R
Exercici [opcional]:
Utilitzant el servidor web apache2 creeu un virtualhost per poder utilitzar symfony2 directament posant: http://localhost/
Estructura del projecte: Aplicacions, Mòduls i Accions
Symfony considera un projecte com un conjunt de serveis i operacions disponibles sota un determinat nom de domini i que comparteixen el mateix model d'objectes.
Dins d'un projecte, les operacions s'agrupen de forma lògica en aplicacions. Normalment, una aplicació s'executa de forma independent respecte d'altres aplicacions del mateix projecte. L'habitual és que un projecte contingui dues aplicacions: una per a la part pública i una altra per a la part de gestió, compartint ambdues la mateixa base de dades. També és possible definir projectes que estiguin formats per diversos llocs web petits, cadascun d'ells considerat com una aplicació. En aquest cas, és important tenir en compte que els enllaços entre aplicacions s'han d'indicar de forma absoluta.
Cada aplicació està formada per un o més mòduls. Un mòdul normalment representa
- a una pàgina web o
- a un grup de pàgines amb un propòsit relacionat.
Per exemple, una aplicació podria tenir mòduls com home, articulos, ajuda, carritoCompra, compte, etc.
Els mòduls emmagatzemen les accions, que representen cadascuna de les operacions que es pot realitzar en un mòdul. Per exemple el mòdul carretCompra pot definir accions com afegir, mostrar i actualitzar. Normalment les accions es descriuen mitjançant verbs. Treballar amb accions és molt similar a treballar amb les pàgines d'una aplicació web tradicional, encara que en aquest cas dues accions diferents poden acabar mostrant la mateixa pàgina (com per exemple l'acció d'afegir un comentari a una entrada d'un blog, que acaba tornant a mostrar la pàgina de l'entrada amb el nou comentari).
Estructura de l'arbre de arxius
Normalment, tots els projectes web comparteixen el mateix tipus de continguts, com per exemple:
Una base de dades, com MySQL o PostgreSQL
- Arxiu estàtics (HTML, imatges, arxius de Javascript, fulles d'estils, etc.)
- Arxius pujats al lloc web per part dels usuaris o els administradors
- Classes i llibreries PHP
- Llibreries externes (scripts desenvolupats per tercers)
- Arxius que s'executen per lots batch files) que normalment són scripts que s'executen via línia de comandos o mitjançant cron
- Arxius de log (les traces que generen les aplicacions i/o el servidor)
- Arxius de configuració
Symfony proporciona una estructura en forma d'arbre d'arxius per organitzar de forma lògica tots aquests continguts, a més de ser consistent amb l'arquitectura MVC utilitzada i amb l'agrupació projecto / aplicació / mòdul. Cada vegada que es crea un nou projecte, aplicació o mòdul, es genera de forma automàtica la part corresponent d'aquesta estructura. A més, l'estructura es pot personalitzar completament, per reorganitzar els arxius i directoris o per complir amb les exigències d'organització d'un client.
Encara que es pot canviar, per defecte totes les aplicacions Symfony tenen la mateixa estructura de directoris senzilla (i recomanada):
- app/: conté la configuració de l'aplicació.
- src/: aquí es troba tot el codi PHP de l'aplicació.
- vendor/: per convenció aquí es guarden totes les llibreries creades per tercers.
- web/: est és el directori web arrel i conté tots els arxius que es poden accedir públicament.
El directori web
El directori web arrel és el lloc on es troben tots els arxius públics i estàtics tals com a imatges, fulles d'estil i arxius Javascript. També és el lloc on es defineixen tots els controladors frontals, com per exemple el següent:
// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
L'arxiu del controlador frontal (app.php en aquest exemple) és l'arxiu PHP que realment s'executa quan utilitzes una aplicació Symfony2 i el seu treball consisteix a arrencar l'aplicació utilitzant una classe del nucli (AppKernel).
Tenir un controlador frontal significa que s'utilitzen URL diferents i més flexibles que les d'una aplicació PHP típica. Quan es disposa d'un controlador frontal, les URL es formaten de la següent manera:
http://localhost/app.php/hello/Ryan
El controlador frontal, app.php, s'executa i la URL interna: /hello/Ryan es dirigeix internament segons la configuració d'encaminament.
Si a més utilitzes el mòdul mod_rewrite d'Apache, pots forçar l'execució de l'arxiu app.php sense necessitat d'incloure-ho en la URL, per la qual cosa així les URL són encara més netes:
http://localhost/hello/Ryan
El directori de l'aplicació (app)
La classe AppKernel és el punt d'entrada principal de l'aplicació i és la responsable de tota la configuració. Com a tal, s'emmagatzema en el directori app/.
Aquesta classe ha d'implementar dos mètodes que defineixen tot el que Symfony necessita saber sobre la teva aplicació. Ni tan sols has de preocupar-te d'aquests mètodes durant l'arrencada — Symfony els emplena per tu amb paràmetres predeterminats.
- registerBundles(): retorna un array amb tots els bundles necessaris per executar l'aplicació.
- registerContainerConfiguration(): carrega l'arxiu de configuració de recursos de l'aplicació (consulta la secció Configurant l'aplicació).
Durant el desenvolupament d'una aplicació, normalment el directori app/ solament els utilitzes per modificar la configuració i els arxius d'encaminament en el directori app/config/.
Aquest directori també conté el directori caché de l'aplicació (app/cache), un directori de logs (app/logs) i un directori per a arxius de recursos globals, tals com a plantilles (app/Resources).
Carga automàtica En arrencar Symfony, s'inclou un arxiu especial anomenat vendor/autoload.php. Aquest arxiu, creat per Composer, s'encarrega de configurar el carregador automàtic de classes, que al seu torn carrega automàticament tots els arxius de la teva aplicació que es trobin en el directori src/ i totes les llibreries externes configurades en l'arxiu composer.json.
Gràcies al carregador automàtic, mai hauràs de preocupar-te d'usar declaracions include o require. Això és possible perquè Composer utilitza namespace o espai de noms d'una classe per determinar la seva ubicació i així inclou automàticament l'arxiu en l'instant en què necessites una classe.
El carregador automàtic ja està configurat per buscar qualsevol de les teves classes PHP en el directori src/. Perquè funcioni la càrrega automàtica, el nom de la classe i la ruta de l'arxiu han de seguir el mateix patró:
Nom de la classe: Acme\HelloBundle\Controller\HelloController Ruta física de l'arxiu: src/Acme/HelloBundle/Controller/HelloController.php
Configuració de l'aplicació
Segons el lloc web oficial de YAML (http://www.yaml.org/), YAML és "un estàndard per serializar dades en qualsevol llenguatge de programació i amb un format fàcil de llegir per part de les persones". Dit d'una altra forma, YAML és un llenguatge molt senzill que permet descriure les dades com en XML, però amb una sintaxi molt més senzilla. YAML és un format especialment útil per descriure dades que poden ser transformats en arrays simples i associatius, com per exemple:
$casa = array(
'familia' => array(
'apellido' => 'García',
'padres' => array('Antonio', 'María'),
'hijos' => array('Jose', 'Manuel', 'Carmen')
),
'direccion' => array(
'numero' => 34,
'calle' => 'Gran Vía',
'ciudad' => 'Cualquiera',
'codigopostal' => '12345'
)
);
//format yml:
casa:
familia:
apellido: García
padres:
- Antonio
- María
hijos:
- Jose
- Manuel
- Carmen
direccion:
numero: 34
calle: Gran Vía
ciudad: Cualquiera
codigopostal: "12345"
YAML és l'acrònim de "YAML Ain't Markup Language" ("YAML No és un Llenguatge de Marcat") i es pronuncia "yamel". El format es porta utilitzant des de 2001 i existeixen utilitats per processar YAML en una gran varietat de llenguatges de programació.
YAML és molt més ràpid d'escriure que XML (ja que no fan falta les etiquetes de tancament i l'ús continu de les cometes) i és molt més poderós que els tradicionals arxius .ini (ja que aquests últims no suporten l'herència i les estructures complexes). Per aquest motiu, Symfony utilitza el format YAML com el llenguatge preferit per emmagatzemar la seva configuració.
Per saber més sobre aquest format pots consultar la següent pàgina web.
El directori font (src)
El directori src/ conté tot el codi real (codi PHP, plantilles, arxius de configuració, estils, etc.) que pertany a la teva aplicació. De fet, en programar una aplicació Symfony, la major part del teu treball es durà a terme dins d'un o més bundles creats en aquest directori.
Els Bundles
Entorn de desenvolupament i entorn de producció
Una aplicació pot funcionar en diversos entorns. Els diferents entorns comparteixen el mateix codi PHP (solament és diferent el controlador frontal), però usen una configuració diferent. Per exemple, un entorn de desenvolupament dev guarda els advertiments i errors, mentre que un entorn de producció prod només registra els errors. Alguns arxius es tornen a generar en cada petició en l'entorn dev (para major comoditat dels desenvolupadors), però s'escorcollen en l'entorn prod. Tots els entorns es troben en la mateixa màquina i executen la mateixa aplicació.
Un projecte Symfony2 normalment comença amb tres entorns (dev, test i prod), encara que resulta senzill crear nous entorns. Pots veure la teva aplicació en diferents entorn amb només canviar el controlador frontalen el teu navegador. Per veure l'aplicació en l'entorn dev, accedeix a l'aplicació a través del controlador frontal de desenvolupament:
http://localhost/app_dev.php/hola
Si desitges veure com es comportarà la teva aplicació a l'entorn de producció, utilitza en el seu lloc el controlador frontal prod:
http://localhost/app.php/hola
Si fas qualsevol canvi en les plantilles, no ho veuràs en l'entorn prod tret que esborris la cache de l'aplicació i així forcis a Symfony a tornar a compilar les plantilles. Per esborrar la cache de l'entorn de producció, executa la següent comanda de consola:
sudo php app/console cache:clear --env=prod && sudo php app/console cache:warmup --env=prod i donar-li permísos: chmod 777 nomprojecte/ -R
El controlador
Un controlador és una funció PHP creada per tu i que s'encarrega d'obtenir la informació de la petició HTTP i de generar i retornar la resposta HTTP (en forma d'objecte de tipus Response de Symfony2). La resposta pot ser:
- una pàgina HTML,
- un document XML,
- un array JSON serializado,
- una imatge,
- una redirecció a una altra pàgina,
- un error de tipus 404
- o qualsevol altra cosa que se t'ocorri.
El controlador conté tota la lògica que la teva aplicació necessita per generar el contingut de la pàgina. L'objectiu d'un controlador sempre és el mateix: crear i retornar un objecte Response.
Cicle de vida d'una petició
- 1.Cada petició és tractada per un únic arxiu: el controlador frontal (per exemple, app.php o app_dev.php) el qual és responsable d'iniciar l'aplicació.
- 2. El sistema d'encaminament (classe Routing) llegeix la informació de la petició (per exemple, la URI), troba una ruta que coincideixi amb aquesta informació, i llegeix el paràmetre _controller de la ruta.
- 3. S'executa el controlador assignat a la ruta i aquest controlador crea i retorna un objecte Response.
- 4. Les capçaleres HTTP i el contingut de l'objecte Response s'envien de tornada al client.
Exemple de controlador:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction()
{
return new Response('<html><body>Hello world!</body></html>');
}
}
Associant una URI a un controlador
El nou controlador retorna una pàgina HTML simple. Per poder provar realment aquesta pàgina en el teu navegador, has de crear una ruta que el seu path sigui la URI que vols associar al controlador al fitxer routing.yml:
hello:
path: /hello
defaults: { _controller: AcmeHelloBundle:Hello:index }
Observa la sintaxi utilitzada per referir-se al controlador: AcmeHelloBundle:Hello:index. Symfony2 utilitza aquesta notació curta per referir-se als controladors. Es tracta de la sintaxi recomanada i li diu a Symfony2 que busqui una classe controlador anomenada HelloController dins d'un paquet anomenat AcmeHelloBundle i que després executi el mètode indexAction().
Passar arguments al mètode del controlador Exemple de controlador amb un argument en un dels seus mètodes:
<?php
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HelloController extends Controller
{
public function indexAction($name)
{
// ...
}
}
El controlador anterior té un sol argument, anomenat $name, el valor del qual correspon al paràmetre {name} de la ruta associada. De fet, quan executes el teu controlador, Symfony2 associa cada argument del controlador amb un paràmetre de la ruta. L'arxiu d'enrutament hauria de ser aquest:
hello:
path: /hello
defaults: { _controller: AcmeHelloBundle:Hello:index }
Considera el següent exemple:
# app/config/routing.yml
hello:
path: /hello/{firstName}/{lastName}
defaults: { _controller: AcmeHelloBundle:Hello:index, color: green }
public function indexAction($firstName, $lastName, $color)
{
// ...
}
Les variables {firstName} i {lastName} de la ruta es diuen placeholders, ja que "guarden el lloc" perquè qualsevol valor substitueixi aquesta variable. D'altra banda, la variable color és una variable de tipus default, ja que el seu valor sempre està definit per a totes les rutes. Existeixen regles amb els arguments (paràmetres):
- Cada argument obligatori del controlador ha de tenir associat un paràmetre en la ruta
- No tots els paràmetres de la ruta han de ser arguments en el teu controlador
- és perfectament vàlid fer que l'argument sigui opcional.
El següent exemple no llançarà una excepció:
public function indexAction($firstName, $lastName, $color, $foo = 'bar')
{
// ...
}
objecte Request com a argument del controlador
Sol ser molt útil disposar en el controlador de l'objecte Request associat a la petició de l'usuari, especialment quan treballes amb formularis. Per fer que Symfony passi aquest objecte automàticament com a argument del controlador, utilitza el següent codi:
use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->handleRequest($request);
// ...
}
Generador de controladors
Si el que volem és generar un nou controlador a la nostra aplicació podem automatitzar el procés utilitzant la següent comanda:
php bin/console generate:controller
Aquesta comanda et demanarà tota la informació necessaria per crear el controlador i els actions (mètodes) necessaris que podràs utilitzar en el fitxer de rutes. Tant si utilitzes la comanda com si no la utilitzes, sempre podràs canviar la classe generada com tu prefereixis.
Encaminament
Una ruta és una associació entre un patró d'URL i un controlador. Suposem per exemple que desitges associar URL de tipus /blog/el meu-post o /blog/tot-sobre-symfony amb un controlador que sigui capaç de buscar i mostrar l'article sol·licitat.
# app/config/routing.yml
blog_show:
path: /blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }
Codi PHP del controlador associat a aquesta ruta:
// 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 = // usa la variable $slug para consultar la base de datos
return $this->render('AcmeBlogBundle:Blog:show.html.twig', array(
'blog' => $blog,
));
}
}
Aquest és l'objectiu del encaminador de Symfony2: associar la URL d'una petició a un controlador
Funcionament
L'objectiu del sistema d'encaminament de Symfony2 és analitzar aquesta URL i determinar què controlador s'ha d'executar. El procés complet consta dels següents passos:
- La petició es processa en el controlador frontal de Symfony2 (per exemple, en l'arxiu app.php).
- El nucli de Symfony2 (conegut com kernel) sol·licita al enrutador que examini la petició.
- El enrutador busca què patró de les rutes de l'aplicació coincideix amb la URL entrant i retorna informació sobre la ruta, incloent el controlador que s'ha d'executar.
- El nucli de Symfony2 executa el controlador, que en última instància, retorna un objecte Response.
Prefix a les rutes
Resulta habitual haver d'afegir un prefix a totes les rutes importades des d'un arxiu extern. Si vols per exemple que el patró de la ruta acme_hello sigui /admin/hello/{name} en comptes de /hello/{name}, afegeix l'opció prefix en importar les rutes:
# app/config/routing.yml
acme_hello:
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
prefix: /admin
El valor indicat en l'opció prefix (en aquest cas /admin) s'afegeix per davant de tots els patrons de les rutes importades des de l'arxiu extern.
Sessions a Symfony2
Symfony2 inclou un objecte de sessió que permet emmagatzemar informació persistent sobre l'usuari, és a dir, informació que es guarda d'una petició a una altra. Per defecte Symfony2 emmagatzema la informació en una cookie usant les sessions natives de PHP.
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$session = $request->getSession();
// guarda un atributo para reutilizarlo durante una
// petición posterior del usuario
$session->set('foo', 'bar');
// obtener el valor de un atributo de la sesión
$foo = $session->get('foo');
// utilizar un valor por defecto si el atributo no existe
$filters = $session->get('filters', array());
}
Plantilles TWIG
Una plantilla és un arxiu de text que pot generar qualsevol altre format basat en text (HTML, XML, CSV, LaTeX, etc.). Les plantilles PHP són les més populars, com la qual mostra el següent exemple:
<!DOCTYPE html>
<html>
<head>
<title>¡Bienvenido a Symfony!</title>
</head>
<body>
<h1><?php echo $page_title ?></h1>
<ul id="navigation">
<?php foreach ($navigation as $item): ?>
<li>
<a href="<?php echo $item->getHref() ?>">
<?php echo $item->getCaption() ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>
Symfony2 inclou un llenguatge de plantilles anomenat Twig que és molt més potent i elegant que PHP. Gràcies a Twig pots crear plantilles molt concises i fàcils de llegir, per la qual cosa a més són fàcils d'entendre per part dels dissenyadors web. Observa el mateix exemple anterior definit com a plantilla Twig:
<!DOCTYPE html>
<html>
<head>
<title>¡Bienvenido a Symfony!</title>
</head>
<body>
<h1>{{ page_title }}</h1>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
</body>
</html>
Twig es basa en dues etiquetes especials:
- { { ... } }: serveix per mostrar el contingut d'una variable o el resultat de realitzar alguna operació o processar alguna expressió. En PHP la construcció equivalent és echo o print.
- {% ... %}: serveix per definir la lògica de la plantilla, és a dir, la part de programació que controla com es mostren els continguts de la plantilla. Entre uns altres, aquesta etiqueta s'empra per a les instruccions if i per als bucles for.
Herència de plantilles i layout
Normalment les plantilles d'un mateix projecte comparteixen molts elements comuns, com per exemple la capçalera, el peu de pàgina, una barra lateral, etc. Symfony2 resol aquest problema de forma molt senzilla: una plantilla pot decorar el contingut d'una altra plantilla.
La idea és exactament la mateixa que l'herència de classes PHP: l'herència de plantilles et permet crear una plantilla basi cridada layout i que conté tots els elements comuns del lloc definits com a blocs. Després, les plantilles filla hereten del layout i emplenen o modifiquen aquests blocs.
En primer lloc, crea un arxiu amb el teu disseny base:
# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}Test Application{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="contenido">
{% block body %}{% endblock %}
</div>
</body>
</html>
Aquesta plantilla defineix l'esquelet d'una pàgina HTML simple de dues columnes. En aquest exemple, es defineixen tres blocs amb l'etiqueta {% block %} (title, sidebar i body)'. Les plantilles filla poden modificar els continguts de cadascun dels blocs o deixar-los tal com estan en la plantilla base.
El següent exemple mostra l'aspecte d'una plantilla filla:
{# 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 %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
La clau de l'herència de plantilles és l'etiqueta {% extends %}. D'aquesta forma el motor de plantilles sap que primer ha de processar la plantilla base, que defineix el disseny de la pàgina i crea diversos blocs de continguts. Després es renderitza la plantilla filla, que reemplaça el contingut dels blocs title i body del pare.
Quan treballis amb l'herència de plantilles, tingues en compte:
- Si inclous l'etiqueta {% extends %} en una plantilla, aquesta ha de ser la primera etiqueta d'aquesta plantilla.
- Quantes més etiquetes {% block %} tinguis en la teva plantilla basi, millor. Recorda que les plantilles filla no tenen l'obligació d'emplenar tots els blocs dels pares, per la qual cosa pots definir tants blocs com vulguis i assignar a cadascun un valor per defecte que sigui lògic per a la major part de les pàgines del lloc. Quants més blocs defineixi el layout, més flexible serà el seu disseny.
- Si còpies i pegues algun contingut en diverses plantilles, segurament serà millor que moguis aquest contingut a algun bloc del layout. En altres casos el millor és col·locar aquest contingut en alguna altra plantilla i incloure-la amb l'etiqueta include sempre que sigui necessari.
- Si vols obtenir el contingut d'algun bloc de la plantilla pare, utilitza la funció { { parent() } }. Això és molt útil quan vols afegir continguts propis a qualsevol altre contingut que el pare pugui haver definit per a aquest bloc:
{% block sidebar %}
<h3>Table of Contents</h3>
{# ... #}
{{ parent() }}
{% endblock %}
Localització de les plantilles
Les plantilles es troben en dos llocs diferents:
- app/Resources/views/: el directori on s'emmagatzemen les plantilles globals de l'aplicació, com per exemple la plantilla del layout i les plantilles utilitzades per redefinir les plantilles incloses en els bundles de Symfony2.
- ruta/fins a/el/bundle/Resources/views/: els bundles emmagatzemen les seves pròpies plantilles en el directori Resources/views/ de cada bundle. La immensa majoria de les plantilles s'emmagatzemen dins d'algun bundle.
Symfony2 identifica a cada plantilla amb la sintaxi especial bundle:controlador:plantilla. Això permet definir diferents tipus de plantilla, cadascuna emmagatzemada en un lloc diferent:
AcmeBlogBundle:Blog:index.html.twig: indica la plantilla d'una pàgina específica. Est és el significat de cadascuna de les tres parts de la cadena:
- AcmeBlogBundle: (bundle) la plantilla es troba dins del bundle AcmeBlogBundle (per exemple, src/Acme/BlogBundle).
- Blog: (controlador) indica que la plantilla s'emmagatzema en el subdirectori Blog de Resources/views.
- index.html.twig: (plantilla) el nom de l'arxiu que guarda la plantilla és index.html.twig. Suposant que AcmeBlogBundle es trobi en src/Acme/BlogBundle, la ruta completa de la plantilla seria src/Acme/BlogBundle/Resources/views/Blog/index.html.twig.
- AcmeBlogBundle::layout.html.twig: indica que és una plantilla global del bundle indicat. Com a falta la part central que es refereix al controlador, aquesta plantilla no es troba dins de cap subdirectori de Resources/views/. En altres paraules, la ruta completa de la plantilla és Resources/views/layout.html.twig dins del bundle AcmeBlogBundle.
- ::base.html.twig: indica que és una plantilla global de l'aplicació. Observa que la cadena comença amb dos parells de dos punts (::), per la qual cosa falta la part del bundle i la del controlador. En altres paraules, aquesta plantilla no es troba en cap bundle sinó directament dins de app/Resources/views/.
Etiquetes Twig
Incloent unes altres plantilles
Resulta habitual voler incloure la mateixa plantilla o fragment de codi a diverses pàgines diferents. Si l'aplicació té per exemple un llistat d'articles, el codi de la plantilla que mostra un article es pot utilitzar a la pàgina de detall de l'article, en una pàgina que mostra els articles més populars, o en una llista dels articles més recents.
En PHP, quan necessites reutilitzar un tros de codi, normalment mous el codi a una nova classe o funció. En les plantilles s'aplica la mateixa idea:
{# src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.twig #}
<h2>{{ article.title }}</h2>
<h3 class="byline">by {{ article.authorName }}</h3>
<p>
{{ article.body }}
</p>
Ara ja pots incloure fàcilment aquesta plantilla en qualsevol una altra:
{# src/Acme/ArticleBundle/Resources/views/Article/list.html.twig #}
{% extends 'AcmeArticleBundle::layout.html.twig' %}
{% block body %}
<h1>Recent Articles<h1>
{% for article in articles %}
{{ include(
'AcmeArticleBundle:Article:articleDetails.html.twig',
{'article': article}
) }}
{% endfor %}
{% endblock %}
Renderitzant plantilles enviant-li dades des de el controlador
Imagina que en el teu lloc web tens una barra lateral que mostra els tres articles més recents. Per obtenir aquests tres articles és necessari realitzar una consulta a la base de dades o alguna altra operació similar que no es pot incloure en la pròpia plantilla.
La solució consisteix a inserir en la plantilla el resultat retornat per un controlador de l'aplicació. En primer lloc, crea un controlador que renderitzi el llistat dels articles recents:
// src/Acme/ArticleBundle/Controller/ArticleController.php
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// hace una llamada a la base de datos u otra lógica
// para obtener los "$max" artículos más recientes
$articles = ...;
return $this->render(
'AcmeArticleBundle:Article:recentList.html.twig',
array('articles' => $articles)
);
}
}
i a la plantilla es pot utilitzar les dades del array enviat des del controlador directament:
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}
Per mostrar en qualsevol plantilla el resultat d'executar un controlador, utilitza la funció render i identifica al controlador utilitzant la notació especial bundle:controlador:acció:
{# app/Resources/views/base.html.twig #}
{# ... #}
<div id="sidebar">
{{ render(controller('AcmeArticleBundle:Article:recentArticles', {
'max': 3
})) }}
</div>
Exemple de IF dintre del twig:
{% if kenny.sick %}
Kenny is sick.
{% elseif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
Enllaçant pàgines
Crear enllaços a altres pàgines de l'aplicació és una de les tasques més comunes d'una plantilla. En lloc de generar a mà les URL dins de la plantilla, utilitza la funció path de Twig (o el helper router en PHP) per generar les URL utilitzant la configuració del sistema de encaminament. D'aquesta manera, si més endavant vols canviar l'aspecte de qualsevol URL, només has d'actualitzar un arxiu de configuració i totes les URL de les plantilles s'actualitzaran instantàniament.
<a href="{{ path('_welcome') }}">Home</a>
<a href="{{ path('hello',{ "id": '2'}) }}">Home</a>
On _welcome està definit al fitxer de routes:
_welcome:
path: /
defaults: { _controller: AcmeDemoBundle:Welcome:index }
Enllaçant arxius web (css, javascript)
Les plantilles de les aplicacions web solen enllaçar amb els web 'assets o arxius web, tals com a imatges, fulles d'estil CSS, arxius Javascript, etc.
A la versió 2.8 ja no s'instal·la assetic per defecte i s'ha d'instal·lar utilitzant aquesta guia: http://symfony.com/doc/current/cookbook/assetic/asset_management.html
Quan passem a producció hem d'executar:
sudo php app/console assetic:dump --env=prod --no-debug
De nou no és aconsellable generar a mà les URL d'aquest tipus d'arxius, ja que Symfony2 ofereix una solució millor i molt més flexible mitjançant la funció asset de Twig:
<img src="{{ asset('images/logo.png') }}" alt="Symfony!" />
<link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" />
La gestió dels arxius CSS i dels arxius Javascript es realitza mitjançant la herència de plantilles. A la plantilla para es pot incloure els arxius que s'utilitzarà a tota la aplicació i després, a les plantilles filles, es poden incloure fitxers específics per aquella part de la web: Plantilla pare:
{# app/Resources/views/base.html.twig #}
<html>
<head>
{# ... #}
{% block stylesheets %}
<link href="{{ asset('css/main.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}
</head>
<body>
{# ... #}
{% block javascripts %}
<script src="{{ asset('js/main.js') }}" type="text/javascript"></script>
{% endblock %}
</body>
</html>
i les plantilles filles redefineixen els blocs tot utilitzant la configuració que hi ha en la plantilla pare:
{# src/Acme/DemoBundle/Resources/views/Contact/contact.html.twig #}
{% extends '::base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('css/contact.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}
{# ... #}
Les plantilles també poden enllaçar arxius que es trobin en el directori Resources/public de qualsevol bundle. Per fer que aquests arxius estiguin disponibles en l'aplicació, executa la comanda:
php app/console assets:install directorio [--symlink]
Si al utilitzar assets en producció no funciona:
http://symfony.com/doc/current/cookbook/assetic/asset_management.html#cookbook-assetic-dumping
Variables globals que es poden utilitzar directament a les plantilles
- app.security - el context de seguretat.
- app.user - l'objecte qeu representa a l'usuari que està visitant l'aplicació.
- app.request - l'objecte Request que conté tota la informació de la petició.
- app.session - l'objecte Session relacionat amb l'usuari.
- app.environment - l'entorn en el qual s'està executant l'aplicació (dev, prod, etc.)
- app.debug - val true si l'aplicació s'està executant en la manera de depuració i false en un altre cas.
Formularis
Guia Symfony per crear i utilitzar formularis: http://symfony.com/doc/2.8/book/forms.html
Symfony incorpora unes llibreries que permeten utilitzar els formularis associant-los amb clases PHP. Exemple: Classe Tasca:
// src/Acme/TaskBundle/Entity/Task.php
namespace Acme\TaskBundle\Entity;
class Task
{
// descripción de la tarea
protected $task;
// fecha en la que debe estar completada
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;
}
}
Per crear el formulari on demanem totes, o algunes, de les dades que necessitem per crear la tasca anterior fariem el següent:
// 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)
{
// crea una task y le asigna algunos datos ficticios para este ejemplo
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'formulari' => $form->createView(),
));
}
}
Utilitzant el formulari en una plantilla TWIG:
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{{ form(formulari) }}
Processant l'enviament del formulari
Exemple del controlador que envia i reb les dades d'un formulari:
public function newAction(Request $request)
{
// crear un objeto $task nuevo (borra los datos de prueba)
$task = new Task();
$form = $this->createFormBuilder($task)
->setAction($this->generateUrl('target_route'))
->setMethod('GET')
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// guardar la tarea en la base de datos
return $this->redirect($this->generateUrl('task_success'));
}
// ...
}
- Quan es carrega per primera vegada la pàgina associada a aquest controlador, es crea i renderiza el formulari. El mètode handleRequest() detecta que el formulari no s'ha enviat i per tant, no fa gens.El mètode isValid() retorna false si el formulari no s'ha enviat.
- Quan l'usuari envia el formulari, el mètode handleRequest() ho detecta i guarda immediatament les dades enviades en les propietats task i dueDate de l'objecte $task. Després es valida aquest objecte. Si no és vàlid, el mètode isValid() retorna false una altra vegada, per la qual cosa es torna a mostrar el formulari, aquesta vegada amb els missatges d'error corresponents. Si solament vols comprovar si el formulari s'ha enviat, independentment de si és vàlid o no, utilitza el mètode isSubmitted().
- Quan l'usuari envia el formulari amb dades vàlides, les dades enviades es guarden de nou en el formulari, però aquesta vegada el mètode isValid() retorna true. En aquest moment ja pots treballar amb l'objecte $task (per exemple guardant-ho en una base de dades) abans de redirigir a l'usuari a una altra pàgina (per exemple a la pàgina d'agraïment o a la qual mostra un missatge determinat).
Seguretat
És un procés de dues etapes que el seu objectiu és evitar que un usuari accedeixi a un recurs pel qual no hauria de tenir accés.
En el primer pas del procés, el sistema de seguretat identifica qui és l'usuari obligant-ho a enviar algun tipus d'identificació. Això es diu autenticació, i significa que el sistema està tractant d'esbrinar qui ets.
Una vegada que el sistema sap qui ets, el següent pas és decidir si hauries de tenir accés a un determinat recurs. Aquesta part del procés es diu autorització, i significa que el sistema està comprovant si tens suficients privilegis per realitzar una determinada acció.
Firewalls (autenticació)
El sistema de seguretat de Symfony s'activa quan un usuari fa una petició a una URL que està protegida per un firewall o tallafocs. El treball del firewall consisteix a determinar si l'usuari necessita estar autenticat, i si ho necessita, enviar una resposta a l'usuari per iniciar el procés d'autenticació.
Un firewall s'activa quan la URL d'una petició entrant concorda amb el valor de la seva opció de configuració pattern. En aquest exemple el valor de pattern (^/) concorda amb qualsevol petició entrant. No obstant això, el fet que el firewall estigui activat no significa que el navegador mostra la caixa de login+contrasenya per a totes les URL. Els usuaris poden accedir per exemple a /foo sense que l'aplicació els demani que s'autentiquin.
Exemple:
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
http_basic:
realm: "Secured Demo Area"
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
# Descomenta la siguiente línea para proteger también
# la propia URL /admin
# - { path: ^/admin$, roles: ROLE_ADMIN }
providers:
in_memory:
memory:
users:
ryan: { password: ryanpass, roles: 'ROLE_USER' }
admin: { password: kitten, roles: 'ROLE_ADMIN' }
encoders:
Symfony\Component\Security\Core\User\User: plaintext
Aquest funcionament és possible en primer lloc perquè el firewall permet l'accés als usuaris anònims a causa de l'opció de configuració anonymous. En altres paraules, el firewall no exigeix que tots els usuaris s'autentiquin res més accedir a l'aplicació. I com en la configuració de la secció access_control no s'indica que els usuaris hagin de tenir cap role especial per accedir a /foo la petició es processa sense requerir a l'usuari que s'autentiqui.
Si elimines l'opció anonymous, l'efecte és que ara el firewall demana autenticació a qualsevol recurs.
Seguint amb el mateix exemple, si un usuari sol·licita /admin/foo, l'aplicació es comporta de manera diferent. Això és a causa de la configuració de la secció access_control, que indica que qualsevol URL que coincideixi amb l'expressió regular ^/admin (és a dir, la URL /admin o qualsevol altra URL que coincideixi amb /admin/) requereix el rol ROLE_ADMIN. Els rols són la clau del sistema d'autorització: l'usuari pot accedir a /admin/foo només si compta amb el rolROLE_ADMIN.
La capa de control d'accés denega l'accés a l'usuari (perquè els usuaris anònims no compten amb el rol ROLE_ADMIN), el firewall pren el control de l'aplicació i inicia el procés d'autenticació.
Utilitzant el típic formulari de accés
En primer lloc, afegeix l'opció form_login en la configuració del firewall:
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: login
check_path: login_check
El sistema de seguretat inicia el procés d'autenticació, es redirigeix a l'usuari a la ruta que mostra el formulari d'accés (per defecte /login). El formulari has de crear-ho tu mateix a mà, ja que Symfony no ho proporciona. Primer crea les dues noves rutes utilitzades en la configuració de la seguretat: la ruta login mostra el formulari (es correspon amb la URL /login) i login_check que processa l'enviament del formulari (es correspon amb la URL /login_check):
# app/config/routing.yml
login:
pattern: /login
defaults: { _controller: AcmeSecurityBundle:Security:login }
login_check:
pattern: /login_check
Observa que el nom de la ruta login coincideix amb el valor de l'opció login_path, ja que és on el sistema de seguretat redirigeix als usuaris que necessiten autenticar-se.
A continuació, crea el controlador que mostra el formulari de accés:
// src/Acme/SecurityBundle/Controller/SecurityController.php;
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
public function loginAction(Request $request)
{
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render(
'AcmeSecurityBundle:Security:login.html.twig',
array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
)
);
}
}
Si l'usuari envia un nom d'usuari o contrasenya no vàlids, aquest controlador obté el missatge d'error del sistema de seguretat i ho mostra a l'usuari. Tu t'encarregues de mostrar el formulari a l'usuari i els errors que puguin haver ocorregut, però el propi sistema de seguretat s'encarrega de verificar el nom d'usuari i contrasenya i l'autenticació de l'usuari. Plantilla del formulari:
{# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
{#
añade lo siguiente si quieres redirigir al usuario a una
URL concreta después del login (explicado más adelante)
<input type="hidden" name="_target_path" value="/account" />
#}
<button type="submit">login</button>
</form>
Webgrafia
Els llibres sobre symfony utilitzats per elaborar un resum de les funcionalitats més importants els podeu trobar a : http://librosweb.es/libros/
També s'ha utilitzat el llibre oficial que podeu trobar a la web:http://symfony.com/