Upload
stefano-maraspin
View
114
Download
5
Embed Size (px)
DESCRIPTION
Introduzione a Zend Framework 2 per chi proviene da Zend Framework 1, tenuta allo Zend Framework Day di Milano del 01/02/2013. Introduzione alle nuove caratteristiche e pattern architetturali di ZF2 Zend Framework 2 non è l'evoluzione di ZF, ma un progetto nuovo: il codice è stato riscritto e poche sono le parti in comune con la versione precedente. Lo sviluppatore abituato a ZF1 non ha vita facile, ed è fondato il timore di dover imparare tutto da capo. In questo talk vediamo come cambiare le vecchie abitudini di sviluppatori ZF1, per sfruttare al meglio le potenzialità del nuovo strumento. Attraverso esempi concreti, in cui vedremo all'opera i nuovi pattern e le best practice, mostriamo come - partendo con il piede giusto - il passaggio a ZF2 possa essere meno traumatico del previsto. Il talk è orientato soprattutto a chi già conosce ZF1, ma gli argomenti affrontati possono essere utili anche a chi si avvicina a ZF per la prima volta.
Citation preview
Zend Framework 2 ciò che facevo con ZF1
Zend Framework Day – Milano – 01/02/2013
Fare con
2
@maraspin
3
http://www.mvassociati.it/
4
http://friuli.grusp.org/
brutto_esempio.php <?php
echo '<html><body>';
$caldaia=$_GET['caldaia'];
$mysqli = new mysqli("localhost", "user", "pwd", "mva");
$query = "SELECT * FROM cespiti WHERE id =".$caldaia;
$result = $mysqli->query($query);
while ($row = $result->fetch_assoc()) {
?><div><?php echo $row["nome, temperatura"]); ?></div>
<?php } ?>
<?php
$result->free();
$mysqli->close();
echo '</html></body>';
?>
IL “FENOMENO”
AGGIORNAMENTO
9
LA SEGRETARIA
UFF. TECNICO
PRODUZIONE
ACCOUNT
14
Com’è strutturata l’azienda?
15
Che in un’applicazione…
16
MVC
17
Flusso MVC
18
Routing
19
Interazione con il modello
20
Il layer di presentazione
21
Risultato all’utente
22
Cosa ci manca?
23
Vorremmo una situazione così
24
Disaccoppiamento
I MODULI
26
Permettono questo
27
Come installare ZF2?
28
THE SKELETON APP
30
Cloning
git clone git://github.com/zendframework/ZendSkeletonApplication.git
31
composer.json {
"name": "zendframework/skeleton-application",
"description": "Skeleton Application for ZF2",
"license": "BSD-3-Clause",
"keywords": [
"framework",
"zf2"
],
"homepage": "http://framework.zend.com/",
"require": {
"php": ">=5.3.3",
"zendframework/zendframework": "2.*"
}
}
composer.json {
"name": "zendframework/skeleton-application",
"description": "Skeleton Application for ZF2",
"license": "BSD-3-Clause",
"keywords": [
"framework",
"zf2"
],
"homepage": "http://framework.zend.com/",
"require": {
"php": ">=5.3.3",
"zendframework/zendframework": "2.*"
}
}
34
Installazione
cd ZendSkeletonApplication
php composer.phar install
35
Installazione
cd ZendSkeletonApplication
php composer.phar install
> Installing zendframework/zendframework (dev-master)
Come impostare il progetto?
36
37
Struttura delle Cartelle
38
Struttura delle Cartelle
Configurazione Applicazione
39
Struttura delle Cartelle
Misc (cache, uploads, …)
40
Struttura delle Cartelle
Moduli
41
Struttura delle Cartelle
Moduli
42
Namespace Module.php Modulo
43
Struttura delle Cartelle
Modulo Application
44
Struttura delle Cartelle
Parte pubblica
DOCUMENT ROOT
DOCUMENT ROOT
Come funziona il bootstrap?
48
index.php <?php
/**
* This makes our life easier when dealing with paths. Everything is
* relative to the application root now.
*/
chdir(dirname(__DIR__));
// Setup autoloading
require 'init_autoloader.php';
// Run the application!
Zend\Mvc\Application::init(require 'config/application.config.php')->run();
Configurazione
50
config/application.config.php
config/application.config.php <?php
return array(
'modules' => array(
'Application',
),
[...]
);
Elenco dei moduli attivati
Configurazione
52
Module.config.php <?php [...]
return array(
'router' => array('routes' => array([…]),
),
'service_manager' => array(
'factories' => array(
'translator' => Zend\I18n\Translator\TranslatorServiceFactory',
),),
[...]
'controllers' => array(
'invokables' => array(
'Application\Controller\Index‘=>'Application\Controller\IndexController'
),),
'view_manager' => array(
[...]
),
);
Configurazione
54
Module.php <?php [...]
class Module implements
AutoloaderProviderInterface,
ConfigProviderInterface,
ServiceProviderInterface {
public function getAutoloaderConfig() {[...]}
public function getConfig($env = null) {
return include __DIR__ . '/config/module.config.php';
}
public function getControllerPluginConfig() {[...]}
public function getViewHelperConfig() {[...]}
public function getServiceConfig() {[...]}
}
Configurazione
56
Configurazione
57
BOOTSTRAP
ROUTING
Dall’url al codice return array( […]
'routes'=> array(
[…]
'route' => '/sostituiscilampadina',
'defaults' => array(
'controller' => 'Application\Controller\Elettricista',
'action' => ‘sostlampadina',
)
[…]
),
'controllers' => array(
'invokables' => array(
'App\Controller\Elettricista' => 'App\Controller\ElettricistaController',
),
),
)
Dall’url al codice return array( […]
'routes'=> array(
[…]
'route' => '/sostituiscilampadina',
'defaults' => array(
'controller' => 'Application\Controller\Elettricista',
'action' => ‘sostlampadina',
)
[…]
),
'controllers' => array(
'invokables' => array(
'App\Controller\Elettricista' => 'App\Controller\ElettricistaController',
),
),
)
Dall’url al codice return array( […]
'routes'=> array(
[…]
'route' => '/sostituiscilampadina',
'defaults' => array(
'controller' => 'Application\Controller\Elettricista',
'action' => ‘sostlampadina',
)
[…]
),
'controllers' => array(
'invokables' => array(
'App\Controller\Elettricista' => 'App\Controller\ElettricistaController',
),
),
)
CONVENTION
VS
CONFIGURATION
Come sono fatti i controller?
64
Controller Semplice in ZF2 <?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
$timestamp = time();
return new ViewModel(array(
'timestamp' => $timestamp
));
}
}
Action <?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
$timestamp = time();
return new ViewModel(array(
'timestamp' => $timestamp
));
}
}
Come interagisco con il model?
67
Controller ZF1 […]
class IdraulicoController extends Zend_Controller_Action {
public function installaCaldaia () {
$operatore = new Operatore();
$esito = $operatore->installaCaldaia();
$this->view->esito =$esito;
}
Scendiamo nel dettaglio […]
class IdraulicoController extends Zend_Controller_Action {
public function installaCaldaia () {
$operatore = new Operatore();
$esito = $operatore->installaCaldaia();
$this->view->esito =$esito;
}
Com’è fatto questo oggetto? […]
Class Operatore {
public function __constructor () {
$this->caldaia = new Caldaia();
}
public function getCaldaia() {
return $this->caldaia;
}
public function installaCaldaia() {
// Fai qualcosa con $this->caldaia
}
}
Dipendenza. Siamo vincolati! […]
Class Operatore {
public function __constructor () {
$this->caldaia = new Caldaia();
}
public function getCaldaia() {
return $this->caldaia;
}
public function installaCaldaia() {
// Fai qualcosa con $this->caldaia
}
}
“IL PRINCIPIO DEL GARZONE”
Siamo vincolati […]
Class Operatore {
public function __constructor (Caldaia $caldaia) {
$this->caldaia = $caldaia;
}
public function getCaldaia() {
return $this->caldaia;
}
public function installaCaldaia() {
// Fai qualcosa con $this->caldaia
}
}
Siamo vincolati […]
Class Operatore {
public function __constructor (Caldaia $caldaia) {
$this->caldaia = $caldaia;
}
public function getCaldaia() {
return $this->caldaia;
}
public function installaCaldaia() {
// Fai qualcosa con $this->caldaia
}
}
Test operatore […]
class OperatoreTest extends TestCase {
function testInstallaCaldaia() {
$caldaia = mockCaldaia();
$operatore = new Operatore($caldaia);
$esito = $operatore->installaCaldaia();
$this->assertTrue($esito);
}
}
Da…
76
A…
77
INVERSIONE DI CONTROLLO
Factory operatore […]
class OperatoreFactory {
public static function create ($nome) {
$caldaia = new Caldaia();
$operatore = new Operatore($caldaia);
return $operatore;
}
}
Controller ZF1 […]
class IdraulicoController extends Zend_Controller_Action {
public function installaCaldaia () {
$I_operatore = OperatoreFactory::create();
$esito = $I_operatore->installaCaldaia();
$this->view->esito =$esito;
}
ZF2 - Il Service Manager […]
class IdraulicoController extends AbstractActionController {
public function installaCaldaia () {
$operatore = $this->serviceLocator->get(‘operatore’);
$esito = $I_operatore->installaCaldaia();
return new ViewModel(‘operatore’ => $operatore,
‘esito’ => $esito);
}
Riconsideriamo la configurazione return array(
...
'controllers' => array(
‘factories' => array(
'App\Controller\Idraulico' =>
'App\Controller\IdraulicoControllerFactory',
),),
‘service_manager’ =>
‘factories' => array(
‘operatore' => ‘MVA\Services\OperatoreFactory',
),
‘invokables' => array(
‘caldaia' => ‘MVA\Services\Caldaia',
),
‘aliases' => array(
‘attrezzi.martelloPneumatico' => ‘attrezzi.martello',
),
)
Factories return array(
...
'controllers' => array(
‘factories' => array(
'App\Controller\Idraulico' =>
'App\Controller\IdraulicoControllerFactory',
),),
‘service_manager’ =>
‘factories' => array(
‘operatore' => ‘MVA\Services\OperatoreFactory',
),
‘invokables' => array(
‘caldaia' => ‘MVA\Services\Caldaia',
),
‘aliases' => array(
‘attrezzi.martelloPneumatico' => ‘attrezzi.martello',
),
)
Invokables return array(
...
'controllers' => array(
‘factories' => array(
'App\Controller\Idraulico' =>
'App\Controller\IdraulicoControllerFactory',
),),
‘service_manager’ =>
‘factories' => array(
‘operatore' => ‘MVA\Services\OperatoreFactory',
),
‘invokables' => array(
‘caldaia' => ‘MVA\Services\Caldaia',
),
‘aliases' => array(
‘attrezzi.martelloPneumatico' => ‘attrezzi.martello',
),
)
LAZY REGISTRY, ON STEROIDS
Service Locator
SE FATTO CON REGISTRY
Service Locator
Service Locator
Uso di Alias return array(
...
'controllers' => array(
‘factories' => array(
'App\Controller\Idraulico' =>
'App\Controller\IdraulicoControllerFactory',
),
),
‘service_manager’ =>
‘invokables' => array(
‘attrezzi.martello' => 'App\Services\Martello',
‘attrezzi.martelloPneumatico‘ => 'App\Services\MartelloPneumatico',
),
)
Uso di Alias return array(
...
'controllers' => array(
‘factories' => array(
'App\Controller\Idraulico' =>
'App\Controller\IdraulicoControllerFactory',
),
),
‘service_manager’ =>
[…]
‘invokables' => array(
‘attrezzi.martello' => 'App\Services\Martello',
‘attrezzi.supermartello' => 'App\Services\Martello',
),
‘aliases' => array(
‘attrezzi.martelloPneumatico' => ‘attrezzi.supermartello',
),
)
I diversi service managers
Diagramma service managers
Peering
Uso del Service Locator? […]
class Termostato implements ServiceLocatorAwareInterface {
[…]
public function getTemperatura() {
$sm = $this->getServiceLocator();
$sensore = $sm->get(‘Azienda\Model\TermoSensore');
return $sensore->getTemperatura();
}
public function setServiceLocator(ServiceLocatorInterface
$serviceLocator) {
$this->serviceLocator = $serviceLocator; }
public function getServiceLocator() {
return $this->serviceLocator; }
}
Abuso del Service Locator […]
class Termostato implements ServiceLocatorAwareInterface {
[…]
public function getTemperatura() {
$sm = $this->getServiceLocator();
$sensore = $sm->get(‘Azienda\Model\TermoSensore');
return $sensore->getTemperatura();
}
public function setServiceLocator(ServiceLocatorInterface
$serviceLocator) {
$this->serviceLocator = $serviceLocator; }
public function getServiceLocator() {
return $this->serviceLocator; }
}
ATTENZIONE!
Abuso del Service Locator […]
class Termostato implements ServiceLocatorAwareInterface {
[…]
public function getTemperatura() {
$sm = $this->getServiceLocator();
$sensore = $sm->get(‘Azienda\Model\TermoSensore');
return $sensore->getTemperatura();
}
public function setServiceLocator(ServiceLocatorInterface
$serviceLocator) {
$this->serviceLocator = $serviceLocator; }
public function getServiceLocator() {
return $this->serviceLocator; }
}
Factory per il Controller […]
Class IdraulicoControllerFactory implements
FactoryInterface {
public static function
createService(ServiceLocatorInterface $services) {
$sm = $services->getServiceLocator();
$operatore = $sm->get(‘Operatore');
return new IdraulicoController($operatore);
}
[…]
}
?>
DEPENDENCY INJECTION
http://ocramius.github.com/blog/zend-framework-2-controllers-and-dependency-injection-with-zend-di/
E per le funzioni di supporto?
101
Scambio di messaggi class IdraulicoController {
public function installaCaldaiaAction() {
$operatore = $this->serviceLocator->get(‘operatore’);
$esito = $I_operatore->installaCaldaia();
$I_logger = new Logger();
$I_logger->log(‘Installazione Caldaia’);
return new ViewModel(‘operatore’ => $operatore,
‘esito’ => $esito);
}
}
Mi trovo nel posto giusto? class IdraulicoController {
public function installaCaldaiaAction() {
$operatore = $this->serviceLocator->get(‘operatore’);
$esito = $I_operatore->installaCaldaia();
$I_logger = new Logger();
$I_logger->log(‘Installazione Caldaia’);
return new ViewModel(‘operatore’ => $operatore,
‘esito’ => $esito);
}
}
Messaggio Diretto
CROSS CUTTING CONCERNS
Problema di estendibilità
$s_msg = ‘Installazione Caldaia’;
$I_logger = new Logger();
$I_logger->log($s_msg);
$I_mailer = new Mailer();
$I_mailer->mail($s_msg);
[…]
Event Manager
Class Segretaria {
public function onFinitoIntervento() {
/* Occupati della Fatturazione */
}
}
Event Manager Class IdraulicoFactory {
createService(ServiceLocatorInterface $serviceLocator) {
$idraulico = new Idraulico ();
$idraulico ->setEventManager(
$serviceLocator->get('eventManager')
);
$segretaria = $serviceLocator->get(‘segretaria');
$idraulico->getEventManager()->attach(
‘finitointervento',
array($segretaria, ‘onFinitoIntervento')
);
return $idraulico;
}
}
Event Manager //nella classe che scatena l’evento...
class Idraulico {
public function installaCaldaia () {
/* Codice Installazione Caldaia */
$this->eventManager
->trigger(‘finitointervento‘,
$this,
array(‘azione’ => ‘Tutto OK!’)
);
}
}
Event Manager
OBSERVER
Event Manager
Come interagisco con il DB?
113
ZEND\DB
115
Come uso controller plugin?
116
Esempio con controller plugin <?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function redirectAction()
{
$this->redirect()->toUrl(‘http://www.zfday.it’);
}
}
Come recupero parametri?
118
Factory per il Controller […]
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function scriviNomeAction()
{
$nome = $this->getEvent()
->getRouteMatch()->getParam('slug');
return (‘nome’ => $nome);
}
}
E se ho funzionalità ripetute?
120
init() <?php
class IdraulicoController extends Zend_Controller_Action {
public function init () {
$this->view->azienda = $this->getParam(‘azienda’);
}
}
Ma non ho ancora l’EM! […]
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function __construct()
{
$this->azienda = $this->getEvent()
->getRouteMatch()->getParam(‘azienda');
}
}
Sulla Dependency Injection… […]
// Sul costruttore
public function __construct($dipendenza) {
$this->dipendenza = $dipendenza;
}
// Con setters
Public function __construct() {
}
Public function setDipendenza() {
$this->dipendenza = $dipendenza;
}
Agisco sul setter […]
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IdraulicoController extends AbstractActionController
{
public function setEventManager(EventManagerInterface $events) {
parent::setEventManager($events);
$controller = $this;
$events->attach('dispatch', function ($e) use ($controller) {
$this->name = $controller->params()->fromRoute(‘azienda',
‘Sconosciuta');
}
}}
Vedasi anche il post di M. W. O’Phinney: http://www.mwop.net/blog/2012-07-30-the-new-init.html
Come funzionano le viste?
125
Il layer di presentazione
View //view index/index.phtml <div class="row">
<!-- Including header partial -->
<?php echo $this->partial('partials/header.phtml', array()); ?>
<!-- Stampiamo la data -->
<h1>Actual time is: <?php echo $this->timestamp;?></h1>
</div>
View con helper //usando il view helper printData //view index/index.phtml
<div class="row">
<!-- Including header partial -->
<?php echo $this->partial('partials/header.phtml', array()); ?>
<!-- Stampiamo la data -->
<h1>Actual time is:
<?php echo $this->printData($this->timestamp);?>
</h1>
</div>
Helper //Creando un viewHelper per formattare la data // Va registrato tra gli invokables del SM dei ViewHelpers
<?php
namespace Application\View\Helper;
class PrintData extends \Zend\View\Helper\AbstractHelper {
public function __invoke($timestamp) {
$date = new \DateTime();
$date->setTimestamp($timestamp);
$result = $date->format('d-m-Y H:i');
return $result;
}
}
Come funzionano le form?
130
Creando una form //Creiamo il file ContactForm dentro nostro modulo Application/src/Form
<?php
namespace Application\Form;
use Zend\Form\Element;
use Zend\Form\Form;
class ContactForm extends Form
{
public function __construct()
{
parent::__construct();
// …
}
//...
}
Form: aggiungiamo i campi public function init() {
$this->setName('contact');
$this->add(array(
'name' => ‘emailMittente',
'type' => 'Zend\Form\Element\Text',
'options' => array(
'label' => 'From:',
),
)
$this->add(array(
'name' => 'Send',
'type' => 'Zend\Form\Element\Submit',
'attributes' => array(
'value' => 'Send',
),); //...
}
}
La Form
Form
Gli InputFilter
Validation: inputFilter //creiamo l'imput filter contactFilter.php dentro Application/src/Form
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\Validator\Hostname as HostnameValidator;
class ContactFilter extends InputFilter
{
public function __construct()
{
//aggiungiamo i filtri
}
}
InputFilter: filter & validation //Esempio filtering(trim) + email validation with custom message
$this->add(array(
'name' => ‘emailMittente',
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'EmailAddress',
'options' => array(
'messages' => array(
\Zend\Validator\EmailAddress::INVALID =>
'L\'indirizzo email inserito non è valido.'),
)
)
)
));
Controller: Usando la form namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Application\Form\Contact;
use Application\Form\ContactFilter;
class IndexController extends AbstractActionController
{
public function contactAction()
{
$form = new Contact();
$filter = new ContactFilter();
$form->setInputFilter($filter);
return new ViewModel(array(
'form' => $form
);
}
}
View: Usando la form <?php
$form = $this->form;
$form->setAttribute('action', $this->url('contact'));
$form->prepare();
echo $this->form()->openTag($form); ?>
<dl class="contact_form">
<dt><?php echo $this->formLabel($form->get('from')); ?></dt>
<dd><?php
echo $this->formInput($form->get('from'));
echo $this->formElementErrors($form->get('from'));
?></dd>
<!-- ... -->
</dl>
<?php echo $this->form()->closeTag($form) ?>;
Una form più filtri
Come posso proseguire?
140
Per Approfondire
141
Per Approfondire
142
Sporcati le mani: https://packages.zendframework.com/docs/latest/manual/en/user-guide/overview.html
143
Segui i migliori eventi
144
http://www.phpday.it
In sintesi 1. Maggior enfasi sul riuso. Tenere a mente
che ci sono i Moduli 2. Su ZF2 si segue maggiormente un
approccio orientato alla configurazione piuttosto che convenzione
3. Lo strumento cerca di favorire il disaccoppiamento (EventManager) e l’inversione di controllo (ServiceManager, DI)
145
Grazie per l’attenzione
Stefano Maraspin @maraspin [email protected]
DOMANDE?
Photo Credits • http://www.flickr.com/photos/cclark395/7671665642/ • http://www.flickr.com/photos/thompsonrivers/8072087088/ • http://www.flickr.com/photos/ter-burg/5807937726/ • http://www.flickr.com/photos/cimmyt/7798631622/ • http://www.flickr.com/photos/reckless_sniper/6568298617/ • http://www.flickr.com/photos/fil/144232588/ • http://www.flickr.com/photos/ponchopenguin/3262049873/ • http://www.flickr.com/photos/hugosimmelink/1791812548/ • http://www.flickr.com/photos/calsidyrose/4925267732/ • http://www.flickr.com/photos/29233640@N07/8412347937/ • http://www.flickr.com/photos/stungeye/2774997317/ • http://www.flickr.com/photos/urbanwoodswalker/4375401057/ • http://www.flickr.com/photos/adelaide_archivist/3262863212/ • http://www.flickr.com/photos/syslfrog/172945973/ • http://www.flickr.com/photos/tognum/6279595321/ • http://www.flickr.com/photos/dominichargreaves/2825624738/ • http://www.flickr.com/photos/lstcaress/502606063/ • http://www.flickr.com/photos/a-g/2128462119/ • http://www.flickr.com/photos/see-through-the-eye-of-g/4087838437/
148
Stefano Maraspin @maraspin [email protected]