SlideShare ist ein Scribd-Unternehmen logo
1 von 76
Downloaden Sie, um offline zu lesen
High Performance Web
Apps con PHP
e Symfony 2
di Giorgio Cefaro ed Eugenio Pombi
Giorgio Cefaro

@giorrrgio
giorgiocefaro.com
Eugenio Pombi

@euxpom
nerd2business.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.yml
parameters:
database_driver: pdo_mysql
database_host:

127.0.0.1

database_port:

~

database_name:
database_user:

pugx_book
root

database_password: sf2
mailer_transport: smtp
mailer_host:

127.0.0.1

mailer_user:

~

mailer_password: ~
locale:

en

secret:

ThisTokenIsNotSoSecretChangeIt
composer
curl -s https://getcomposer.org/installer | php
composer.json
composer.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 bundle
php 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:nthchild(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:nthchild(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) PUGXBookBundleTestsControllerDefaultControllerTest::testIndex
InvalidArgumentException: The current node list is empty.
/home/eux/Documents/www/symfony2/pugx_book/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Cra
wler.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() in
src/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 - ORM
Object Relational Mapping

class Book
{
public $title;
public $author;
public $publicationDate
}
Doctrine DBAL
Application
Doctrine DBAL
PDO

PostgreSQL

MySQL

SQLite
Book entity
/**
* @ORMEntity
* @ORMTable(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_mysql
database_host:
127.0.0.1
database_port:
~
database_name:
pugx_book
database_user:
root
database_password: ~
Doctrine commands
php app/console doctrine:database:create
php app/console doctrine:database:drop --force
php app/console doctrine:schema:create
Doctrine migrations
directory:
/app/DoctrineMigrations
table:
migration_versions
php app/console doctrine:migrations:diff
php app/console doctrine:migrations:migrate
php app/console doctrine:migrations:execute --up NNN
php app/console doctrine:migrations:execute --down NNN
Doctrine fixtures
src/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/bookDeta
il.html.twig
tag cap5

git checkout cap5
doctrine - approfondimenti
Author è un campo testuale
Un autore è associato a più libri
Spostiamo 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
/**
* @ORMEntity
* @ORMTable(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 - associazioni
in src/PUGX/BookBundle/Entity/Author.php
/**
* @ORMOneToMany(targetEntity="PUGXBookBundleEntityBook",
mappedBy="author")
*/
private $books;

aggiungendo i metodi addBook, getBooks,
setBooks e removeBook
doctrine - associazioni
in src/PUGX/BookBundle/Entity/Book.php
/**
* @ORMManyToOne(targetEntity="PUGXBookBundleEntityAuthor",
inversedBy="books")
**/
protected $author;

aggiungendo i metodi setAuthor e getAuthor
doctrine - associazioni
Osservazione
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 - migrazione
Per poter allineare il database ai cambiamenti, è
necessario generare e applicare una nuova migrazione:
php app/console doctrine:migrations:diff
php 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
/**
*
* @ORMEntity(repositoryClass="PUGXBookBundleRepositoryBookRepository")
* @ORMTable(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();
DQL
public 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 extra1
git checkout extra2
git checkout extra3

Weitere ähnliche Inhalte

Andere mochten auch

Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )
Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )
Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )Genex Logistics
 
Windowsのパケットモニタ作成
Windowsのパケットモニタ作成Windowsのパケットモニタ作成
Windowsのパケットモニタ作成Shinichi Hirauchi
 
HTMLを1行も書かずにwebアプリを作ってみました
HTMLを1行も書かずにwebアプリを作ってみましたHTMLを1行も書かずにwebアプリを作ってみました
HTMLを1行も書かずにwebアプリを作ってみましたShinichi Hirauchi
 
初めてのFacebookアプリの開発
初めてのFacebookアプリの開発初めてのFacebookアプリの開発
初めてのFacebookアプリの開発Shinichi Hirauchi
 
C#で作成するfacebookアプリ mvp community camp
C#で作成するfacebookアプリ mvp community campC#で作成するfacebookアプリ mvp community camp
C#で作成するfacebookアプリ mvp community campShinichi Hirauchi
 
簡易電話交換機の作成~廃品利用による低予算プロジェクト
簡易電話交換機の作成~廃品利用による低予算プロジェクト簡易電話交換機の作成~廃品利用による低予算プロジェクト
簡易電話交換機の作成~廃品利用による低予算プロジェクトShinichi Hirauchi
 
20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門Shinichi Hirauchi
 

Andere mochten auch (10)

Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )
Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )
Genex Logistics Got Covered in BharatBenz (On page no. 8, 14 & 15 )
 
Windowsのパケットモニタ作成
Windowsのパケットモニタ作成Windowsのパケットモニタ作成
Windowsのパケットモニタ作成
 
ReSharperでLinq変換
ReSharperでLinq変換ReSharperでLinq変換
ReSharperでLinq変換
 
HTMLを1行も書かずにwebアプリを作ってみました
HTMLを1行も書かずにwebアプリを作ってみましたHTMLを1行も書かずにwebアプリを作ってみました
HTMLを1行も書かずにwebアプリを作ってみました
 
マウスでタッチ操作
マウスでタッチ操作マウスでタッチ操作
マウスでタッチ操作
 
初めてのFacebookアプリの開発
初めてのFacebookアプリの開発初めてのFacebookアプリの開発
初めてのFacebookアプリの開発
 
C#で作成するfacebookアプリ mvp community camp
C#で作成するfacebookアプリ mvp community campC#で作成するfacebookアプリ mvp community camp
C#で作成するfacebookアプリ mvp community camp
 
簡易電話交換機の作成~廃品利用による低予算プロジェクト
簡易電話交換機の作成~廃品利用による低予算プロジェクト簡易電話交換機の作成~廃品利用による低予算プロジェクト
簡易電話交換機の作成~廃品利用による低予算プロジェクト
 
Kirloskar group
Kirloskar group Kirloskar group
Kirloskar group
 
20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門
 

Ähnlich wie Codemotion workshop

Giovambattista Fazioli, 10 more things
Giovambattista Fazioli, 10 more thingsGiovambattista Fazioli, 10 more things
Giovambattista Fazioli, 10 more thingsKnowCamp
 
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018Marco Chiesi
 
Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Luca Lusso
 
ASP.NET MVC 3 - Presentare i dati nella View
ASP.NET MVC 3 - Presentare i dati nella ViewASP.NET MVC 3 - Presentare i dati nella View
ASP.NET MVC 3 - Presentare i dati nella ViewManuel Scapolan
 
Javascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerJavascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerMatteo Magni
 
Qt Lezione3: un visualizzatore di immagini
Qt Lezione3: un visualizzatore di immaginiQt Lezione3: un visualizzatore di immagini
Qt Lezione3: un visualizzatore di immaginiPaolo Sereno
 
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019Matteo Enna
 
Javascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerJavascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerMatteo Magni
 
Come Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagineCome Drupal costruisce le tue pagine
Come Drupal costruisce le tue paginesparkfabrik
 
Introduzione a..django
Introduzione a..djangoIntroduzione a..django
Introduzione a..djangoLuca Forzutti
 
Progettazione e sviluppo di applicazioni web 2.0 con PHP e Ajax
Progettazione e sviluppo di applicazioni web 2.0 con PHP e AjaxProgettazione e sviluppo di applicazioni web 2.0 con PHP e Ajax
Progettazione e sviluppo di applicazioni web 2.0 con PHP e AjaxGiovanni Cappellini
 
Drupal 7 : theming avanzato
Drupal 7 : theming avanzatoDrupal 7 : theming avanzato
Drupal 7 : theming avanzatoTwinbit
 
Primo Incontro Con Scala
Primo Incontro Con ScalaPrimo Incontro Con Scala
Primo Incontro Con ScalaFranco Lombardo
 
Rich Ajax Web Interfaces in Jquery
Rich Ajax Web Interfaces in JqueryRich Ajax Web Interfaces in Jquery
Rich Ajax Web Interfaces in JqueryAlberto Buschettu
 

Ähnlich wie Codemotion workshop (20)

introduzione a symfony 2
introduzione a symfony 2 introduzione a symfony 2
introduzione a symfony 2
 
Giovambattista Fazioli, 10 more things
Giovambattista Fazioli, 10 more thingsGiovambattista Fazioli, 10 more things
Giovambattista Fazioli, 10 more things
 
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018
Sviluppare un plugin WordPress da zero - WordCamp Bologna 2018
 
Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8
 
Laravel Framework PHP
Laravel Framework PHPLaravel Framework PHP
Laravel Framework PHP
 
ASP.NET MVC 3 - Presentare i dati nella View
ASP.NET MVC 3 - Presentare i dati nella ViewASP.NET MVC 3 - Presentare i dati nella View
ASP.NET MVC 3 - Presentare i dati nella View
 
Javascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerJavascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesigner
 
TYPO3 CMS 7.6 - Le novita
TYPO3 CMS 7.6 - Le novitaTYPO3 CMS 7.6 - Le novita
TYPO3 CMS 7.6 - Le novita
 
Qt Lezione3: un visualizzatore di immagini
Qt Lezione3: un visualizzatore di immaginiQt Lezione3: un visualizzatore di immagini
Qt Lezione3: un visualizzatore di immagini
 
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019
Drupal diventa un CMF e WordPress che fa? Slide WordCamp Milano 2019
 
Javascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesignerJavascript - 4 | WebMaster & WebDesigner
Javascript - 4 | WebMaster & WebDesigner
 
TYPO3 CMS 8.1 - Le novità
TYPO3 CMS 8.1 - Le novitàTYPO3 CMS 8.1 - Le novità
TYPO3 CMS 8.1 - Le novità
 
Come Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagineCome Drupal costruisce le tue pagine
Come Drupal costruisce le tue pagine
 
Introduzione a..django
Introduzione a..djangoIntroduzione a..django
Introduzione a..django
 
TYPO3 CMS 7.3 - le novita
TYPO3 CMS 7.3 - le novitaTYPO3 CMS 7.3 - le novita
TYPO3 CMS 7.3 - le novita
 
Progettazione e sviluppo di applicazioni web 2.0 con PHP e Ajax
Progettazione e sviluppo di applicazioni web 2.0 con PHP e AjaxProgettazione e sviluppo di applicazioni web 2.0 con PHP e Ajax
Progettazione e sviluppo di applicazioni web 2.0 con PHP e Ajax
 
Drupal 7 : theming avanzato
Drupal 7 : theming avanzatoDrupal 7 : theming avanzato
Drupal 7 : theming avanzato
 
react-it.pdf
react-it.pdfreact-it.pdf
react-it.pdf
 
Primo Incontro Con Scala
Primo Incontro Con ScalaPrimo Incontro Con Scala
Primo Incontro Con Scala
 
Rich Ajax Web Interfaces in Jquery
Rich Ajax Web Interfaces in JqueryRich Ajax Web Interfaces in Jquery
Rich Ajax Web Interfaces in Jquery
 

Codemotion workshop