codemotion workshop

Post on 27-Jan-2015

130 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

slides of the workshop held at codemotion rome 2013

TRANSCRIPT

High Performance Web Apps con PHP e Symfony 2

di Giorgio Cefaro ed Eugenio Pombi

Giorgio Cefaro

@giorrrgiogiorgiocefaro.com

Eugenio Pombi

@euxpomnerd2business.net

Symfony 2

First, Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP components that solve common web development problems. Then, based on these components, Symfony2 is also a full-stack web framework. Fabien Potencier

Request > Response

http://symfony.com/download

get the git repo/home/sf2/pugx_book

git clone git@bitbucket.org:eux/pugx_book_2.git

git tag -l

git checkout {nomeTag}

git stash

nerd2business.net

http://pugx-book.localhost/config.php

tag cap1

git checkout cap1

parameters-dist.ymlparameters: database_driver: pdo_mysql database_host: 127.0.0.1 database_port: ~ database_name: pugx_book database_user: root database_password: sf2

mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: ~ mailer_password: ~

locale: en secret: ThisTokenIsNotSoSecretChangeIt

composercurl -s https://getcomposer.org/installer | php

composer.jsoncomposer.lock

./composer.phar update

./composer.phar install

virtual host<VirtualHost *:80> ServerName pugx-book.localhost DocumentRoot "/PATH TO PROJECT/pugx_book/web" DirectoryIndex index.php <Directory "/PATH TO PROJECT/pugx_book/web"> AllowOverride All Require all granted </Directory></VirtualHost>

/etc/hosts

127.0.0.1 pugx-book.localhost

struttura

bundle

tag cap2

git checkout cap2

il nostro bundlephp app/console generate:bundle --namespace=PUGX/BookBundle

--format=yml

Bundle namespace [PUGX/BookBundle]:Bundle name [PUGXBookBundle]:Target directory [/home/USERNAME/PATH/pugx_book/src]:Configuration format (yml, xml, php, or annotation) [yml]:

Do you want to generate the whole directory structure [no]? yes

DefaultControllerTest.php

DefaultControllerTest.php

src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

phpunit -c app/

front controller/web/app.php/web/app_dev.php

/app/config/config.yml/app/config/config_dev.yml/app/config/config_prod.yml/app/config/config_test.yml

routing/app/config/routing.yml

pugx_book:

resource: "@PUGXBookBundle/Resources/config/routing.yml"

prefix: /

/src/PUGX/BookBundle/Resources/config/routing.yml

pugx_book_homepage:

pattern: /

defaults: { _controller: PUGXBookBundle:Default:index }

controller

In genere costituito da una classe che raggruppa una serie di azioni definite attraverso metodi pubblici.

Il nostro primo controller:src/PUGX/BookBundle/Controller/DefaultController.php

twig

return $this->render('PUGXBookBundle:Default:index.html.twig');

PUGXBookBundle: <NomeVendor><NomeBundle>

Default: <NomeController>

index.html.twig: <NomeTemplate>

//src/PUGX/BookBundle/Resources/views/Default/index.html.twig

Hello world!

http://symfony.com/it/doc/2.3/book/templating.html

yay! live coding

//src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

$this->assertTrue($client->getResponse()->isSuccessful());

$this->assertRegExp("/Welcome/i", $crawler->filter('h1')->text());

$this->assertTrue($crawler->filter('p')->count() > 0);

//nav bar

$this->assertTrue($crawler->filter('.navbar')->count() > 0);

$this->assertEquals(1, $crawler->filter('.navbar > li')->count());

$this->assertRegExp("/home/i", $crawler->filter('.navbar > li:nth-child(1)')->text());

PHPUnit asserts

verifichiamo che la risposta sia valida$this->assertTrue($client->getResponse()->isSuccessful());

ci assicuriamo che l’elemento h1 contenga la parola "Welcome":$this->assertRegExp("/Welcome/i", $crawler->filter('h1')->text());

$crawler è un'istanza del componente DomCrawler di Sf2, permette di manipolare documenti XML e HTML attraverso xpath e css selector:http://symfony.com/doc/2.3/components/dom_crawler.html

PHPUnit asserts

ci assicuriamo che ci sia almeno un elemento p nella pagina di risposta:$this->assertTrue($crawler->filter('p')->count() > 0);

verifichiamo che ci sia un oggetto del DOM che abbia la classe css navbar:$this->assertTrue($crawler->filter('.navbar')->count() > 0);

verifichiamo che la navbar contenga un solo elemento li:$this->assertEquals(1, $crawler->filter('.navbar > li')->count());

infine verifichiamo che il primo elemento li di navbar contenga il testo home$this->assertRegExp("/home/i", $crawler->filter('.navbar > li:nth-child(1)')->text());

DefaultControllerTest.php

DefaultControllerTest.php

src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

phpunit -c app/

DefaultControllerTest.php

DefaultControllerTest.php

TEST ROSSI

There was 1 error:

1) PUGX\BookBundle\Tests\Controller\DefaultControllerTest::testIndex

InvalidArgumentException: The current node list is empty.

/home/eux/Documents/www/symfony2/pugx_book/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Crawler.php:468

/home/eux/Documents/www/symfony2/pugx_book/src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php:16

FAILURES!

Tests: 1, Assertions: 1, Failures: 1.

make it GREEN//src/PUGX/BookBundle/Resources/views/Default/index.html.twig

<!DOCTYPE html>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>PUGX Book</title>

</head>

<body>

<div id="sidebar">

<ul class="navbar">

<li><a href="/">Home</a></li>

</ul> </div> <div id="content">

<h1>Welcome on PUGX Book</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultrices, nisi quis porta fermentum, magna ligula suscipit metus, quis blandit leo urna non diam. Sed non dui dui, quis porttitor massa. Phasellus convallis porta leo, sed vehicula eros ultrices sit amet.</p> </div> </body></html>

DefaultControllerTest.php

DefaultControllerTest.php

src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

phpunit -c app/

DefaultControllerTest.php

DefaultControllerTest.php

TEST VERDI

PHPUnit 3.6.11 by Sebastian Bergmann.

Configuration read from /home/giorgio/Progetti/codemotion/pugx_book/app/phpunit.xml.dist

.

Time: 11 seconds, Memory: 17.50Mb

OK (1 test, 6 assertions)

tag cap3

git stash

git checkout cap3

DefaultControllerTest.php

un elenco di libri

Vogliamo aggiungere una pagina che contiene una lista di libri, ognuno con informazioni relative all'autore e alla data di pubblicazione.

DefaultControllerTest.php

Il test

Scriviamo innanzi tutto il test testBooks() insrc/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

DefaultControllerTest.php

l'entità Book

La struttura dati che rappresenta il nostro libro è l'entità Book, definita in:src/PUGX/BookBundle/Entity/Book.php

DefaultControllerTest.php

la nuova rotta

Aggiungiamo una nuova rotta che risponderà all'url /books

DefaultControllerTest.php

la nuova action

Aggiungiamo un metodo booksAction al DefaultController in cui ci limiteremo a creare tre instanze di Book, passandole al twig

DefaultControllerTest.php

il nuovo template

Creiamo un nuovo twig in src/PUGX/BookBundle/Resources/views/Default/books.html.twig

e stampiamo la lista di libri in una tabella

Ereditarietà dei template

tag cap4

git checkout cap4

Doctrine - ORMObject Relational Mapping

class Book { public $title; public $author; public $publicationDate}

Doctrine DBAL

PostgreSQL MySQL SQLite

PDO

Doctrine DBAL

Application

Book entity/** * @ORM\Entity * @ORM\Table(name="book") */class Book{ [...] }

src/PUGX/BookBundle/Entity/Book.php

http://symfony.com/it/doc/2.3/book/doctrine.html#book-doctrine-field-types

parameters.yml/app/config/parameters.yml

database_driver: pdo_mysqldatabase_host: 127.0.0.1database_port: ~database_name: pugx_bookdatabase_user: rootdatabase_password: ~

Doctrine commandsphp app/console doctrine:database:create

php app/console doctrine:database:drop --force

php app/console doctrine:schema:create

Doctrine migrationsdirectory:/app/DoctrineMigrations

table:migration_versions

php app/console doctrine:migrations:diffphp app/console doctrine:migrations:migratephp app/console doctrine:migrations:execute --up NNNphp app/console doctrine:migrations:execute --down NNN

Doctrine fixturessrc/PUGX/BookBundle/DataFixtures/ORM/LoadBookData.php

php app/console doctrine:fixtures:load

DefaultControllerTest.php/src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

Controlliamo che i libri siano in ordine alfabetico

Query con doctrine/src/PUGX/BookBundle/Controller/DefaultController.php

public function booksAction(){ $em = $this->getDoctrine()->getManager(); $books = $em->getRepository('PUGXBookBundle:Book')->findBy( array(), array('title' => 'ASC') ); return $this->render('PUGXBookBundle:Default:books.html.twig', array('books' => $books) );}

DefaultControllerTest.php/src/PUGX/BookBundle/Tests/Controller/DefaultControllerTest.php

Aggiungiamo il test per una pagina di dettaglio (o 404)

rotta - action - template/src/PUGX/BookBundle/Resources/config/routing.yml

/src/PUGX/BookBundle/Controller/DefaultController.php

/src/PUGX/BookBundle/Resources/views/Default/bookDetail.html.twig

tag cap5

git checkout cap5

doctrine - approfondimentiAuthor è un campo testualeUn autore è associato a più libriSpostiamo l'autore in una entità separata.

php app/console doctrine:generate:entity --entity="PUGXBookBundle:Author" --fields="name:string(255) surname:string(255) bio:text"

Author entity/** * @ORM\Entity * @ORM\Table(name="author") */class Author{ [...] }

src/PUGX/BookBundle/Entity/Author.php

http://symfony.com/it/doc/2.3/book/doctrine.html#book-doctrine-field-types

doctrine - associazioni

Per definire l'associazione tra Book e Author utilizziamo le annotazioni di Doctrine OneToMany(lato Author) e ManyToOne(lato Book)

doctrine - associazioniin src/PUGX/BookBundle/Entity/Author.php

/**

* @ORM\OneToMany(targetEntity="PUGX\BookBundle\Entity\Book", mappedBy="author")

*/

private $books;

aggiungendo i metodi addBook, getBooks, setBooks e removeBook

doctrine - associazioniin src/PUGX/BookBundle/Entity/Book.php

/**

* @ORM\ManyToOne(targetEntity="PUGX\BookBundle\Entity\Author", inversedBy="books")

**/

protected $author;

aggiungendo i metodi setAuthor e getAuthor

doctrine - associazioniOsservazione

Doctrine nasconde la logica dell'associazione a livello database, in cui l'associazione è definita da un campo author_id della tabella book e una foreign key. La parte inversa dell'associazione è ricostruita automaticamente da Doctrine.

doctrine - migrazionePer poter allineare il database ai cambiamenti, è necessario generare e applicare una nuova migrazione:

php app/console doctrine:migrations:diffphp app/console doctrine:migrations:migrate

doctrine - fixtures/src/PUGX/BookBundle/DataFixtures/ORM/LoadAuthorData.php

elemento$this->addReference('author-beck', $author);

riferimento$this->getReference('author-beck')

public function getOrder() { return 1;}

template

/src/PUGX/BookBundle/Resources/views/Default/books.html.twig

/src/PUGX/BookBundle/Resources/views/Default/bookDetail.html.twig

{{ book.author.name }} {{ book.author.surname }}

problema!!!

causa

profiler$client->enableProfiler();

[...]

$nbQuery = $client->getProfile()->getCollector('db')->getQueryCount();

$this->assertEquals(1, $nbQuery);

custom repository/** * * @ORM\Entity(repositoryClass="PUGX\BookBundle\Repository\BookRepository") * @ORM\Table(name="book") */class Book

/src/PUGX/BookBundle/Repository/BookRepository.php

/src/PUGX/BookBundle/Tests/Repository/BookRepositoryTest.php

/src/PUGX/BookBundle/Controller/DefaultController.php$books = $em->getRepository('PUGXBookBundle:Book')->findAllWithAuthors();

DQLpublic function findAllWithAuthors(){ $query = $this->getEntityManager() ->createQuery('

SELECT b, a FROM PUGXBookBundle:Book b INNER JOIN b.author a ORDER BY b.title ASC ');

return $query->getResult();}

tag cap6

git checkout cap6

DefaultControllerTest

testCreate

form type

/src/PUGX/BookBundle/Form/Type/BookType.php

routing problem/src/PUGX/BookBundle/Resources/config/routing.yml

pugx_book_detail: pattern: /books/{bookId} defaults: { _controller: PUGXBookBundle:Default:bookDetail }

pugx_book_create: pattern: /books/create defaults: { _controller: PUGXBookBundle:Default:bookCreate }

solution/src/PUGX/BookBundle/Resources/config/routing.yml

pugx_book_detail: pattern: /books/{bookId} defaults: { _controller: PUGXBookBundle:Default:bookDetail } requirements: bookId: \d+

pugx_book_create: pattern: /books/create defaults: { _controller: PUGXBookBundle:Default:bookCreate }

form action/src/PUGX/BookBundle/Controller/DefaultController.php

public function bookCreateAction(Request $request) { ...}

twig form

/BookBundle/Resources/views/Default/bookCreate.html.twig

flash messages

$this->get('session')->getFlashBag()->add('notice', 'Book successfully created');

{% for flashMessage in app.session.flashbag.get('notice') %} <div class="notice"> {{ flashMessage }} </div>{% endfor %}

tearDown

Eliminazione del record inserito con il test

tag cap7

git checkout cap7

security

extra

git checkout extra1git checkout extra2git checkout extra3

top related