1 of 65

LEVEL UP!

Migrating your ZF1 app to ZF2

2 of 65

Gary Hockin

@GeeH on Twitter

GeeH on IRC (Freenode #zftalk)

gary@hock.in

ZF1 and ZF2 Contributor

ZF2 Docs Maintainer

Yolo Certified Engineer

Roave Team Member

3 of 65

4 of 65

The App

5 of 65

6 of 65

The Code

7 of 65

The App

  • Runs in an iFrame
  • ZF1
  • Super Simple
  • Uses
    • Zend_Db
    • Zend_Form,
    • Helpers
    • Config
    • Zend_Registry
    • Front Controller Plugin
  • Works

8 of 65

Why?

  • MODULES BABY!
  • New Architecture
  • Performance
  • Maintainability
  • Cool
  • Why Not?

9 of 65

How?

10 of 65

Strangler

http://www.moviepostershop.com/the-brighton-strangler-movie-poster-1945

11 of 65

BIG BANG!

http://www.amazon.co.uk/Big-Bang-Theory-Season-Complete/dp/B0028BAWU2

12 of 65

You do have service layers, right???

13 of 65

Installing ZF2

composer.phar create-project -sdev --repository-url="https://packages.zendframework.com" zendframework/skeleton-application path/to/install

https://github.com/zendframework/ZendSkeletonApplication

http://zf2.readthedocs.org/en/latest/user-guide/overview.html

14 of 65

15 of 65

Modules

16 of 65

Modules in ZF1

http://www.flickr.com/photos/redux/4298421692

17 of 65

Modules in ZF2

http://www.flickr.com/photos/boltofblue/5460151040/

18 of 65

Project Layout

ZF1 ZF2

application

|- configs

|- controllers

|- IndexController.php

|- forms

|- Comment.php

|- layouts

|- scripts

|- layout.php

|- models

|- DbTable

|- Comment.php

|- Comment.php

|- CommentService.php

|- plugins

|- CallerPlugin.php

|- views

|- helpers

|- DayDifference.php

|- scripts

|- index

|- index.phtml

|- Bootstrap.php

config

module

|- Application

|- config

|- module.config.php

|- src

|- Application

|- Controller

|- IndexController.php

|- Form

|- Comment.php

|- Model

|- Comment.php

|- CommentService.php

|- View

|- Helper

DayDifference.php

|- view

|- application

|- index

|- index.phtml

|- layout

|- layout.phtml

|- Module.php

19 of 65

Project Layout

ZF1 ZF2

application

|- configs

|- controllers

|- IndexController.php

|- forms

|- Comment.php

|- layouts

|- scripts

|- layout.php

|- models

|- DbTable

|- Comment.php

|- Comment.php

|- CommentService.php

|- plugins

|- CallerPlugin.php

|- views

|- helpers

|- DayDifference.php

|- scripts

|- index

|- index.phtml

|- Bootstrap.php

config

module

|- Application

|- config

|- module.config.php

|- src

|- Application

|- Controller

|- IndexController.php

|- Form

|- Comment.php

|- Model

|- Comment.php

|- CommentService.php

|- View

|- Helper

DayDifference.php

|- view

|- application

|- index

|- index.phtml

|- layout

|- layout.phtml

|- Module.php

20 of 65

The Assets

Copy from ZF1:

/public

to ZF2:

/public

Ignoring index.php and .htaccess

(difficult)

21 of 65

The View

Separate object for the view data layer (ViewModel)

View rendering layer renamed to ViewRenderer

(catchy)

22 of 65

The Layout

Copy from ZF1:

application/layouts/scripts/layout.phtml

to ZF2:

module/Application/view/layout/

(yay!)

23 of 65

The Layout

Edit:

module/Application/view/layout/

Change

echo $this->layout()->content;

to

echo $this->content;

(the page should now render like...)

24 of 65

25 of 65

The Models

Copy from ZF1:

application/models/Comment.php

application/models/CommentService.php

to ZF2:

module/Application/src/Application/Model

26 of 65

Namespaces

27 of 65

Namespaces

Goodbye

Long_Underscore_Seperated_Class

Hello

Nice\Snappy\Namespaced\Class

28 of 65

The Models

Update the Services:

ZF1 - Application_Model_Comment

class Application_Model_Comment

{

ZF2 - Application\Model\Comment

namespace Application\Model;

class Comment

{

29 of 65

The Models

Update the Services:

ZF1 - Application_Model_CommentService

class Application_Model_CommentService

{

function __construct(

Application_Model_DbTable_Comment $commentTable)

30 of 65

The Models

Update the Services:

ZF2 - Application\Model\CommentService

namespace Application\Model;

use Zend\Db\TableGateway\TableGateway;

class CommentService

{

function __construct(TableGateway $commentTable)

31 of 65

The Form

http://hock.in/11SCIvU

32 of 65

The Form

Copy

application/forms/Comment.php

to

module/Application/src/Application/Form

33 of 65

The Form

Edit the form to update namespaces

(everything we copy will need this)

ZF1 - Application_Form_Comment

class Application_Form_Comment extends Zend_Form

{

ZF2 - Application\Form\Comment

namespace Application\Form;

use Zend\Form\Form;

class Comment extends Form

{

(last time I’m showing this)

34 of 65

The Form

Rewrite the entire thing

init() becomes __construct()

$this->setMethod('post');

becomes

$this->setAttribute('method', 'post');

35 of 65

The Form

$this->addElement(

'text',

'name',

array(

'label' => 'Your name',

'required' => true,

'filters' => array(

'StringTrim'

),

'validators' => array(

array(

'validator' => 'StringLength',

'options' => array(2, 128)

),

),

)

);

36 of 65

The Form

becomes

$this->add(

array(

'name' => 'name',

'attributes' => array(

'type' => 'text'

),

'options' => array(

'label' => 'Your name ',

),

)

);

37 of 65

The Form

and

$inputFilter = new InputFilter();

$inputFilter->add(array(

'name' => 'name',

'required' => true,

'filters' => array(array(

'name' => 'StringTrim',

)),

'validators' => array(array(

'name' => 'StringLength',

'options' => array(

'min' => 2,

'max' => 128,

)),

),

));

(excuse the formatting)

38 of 65

The Database

ZF1 - application.ini

resources.db.adapter = PDO_MYSQL

resources.db.isDefaultTableAdapter = true

resources.db.params.host = 127.0.0.1

resources.db.params.username = root

resources.db.params.password =

resources.db.params.dbname = commentizr

39 of 65

Intermission

Service Manager

http://sourceforge.net/apps/mediawiki/pagmo/nfs/project/p/pa/pagmo/1/15/Don_t_panic.jpg

40 of 65

The Database

ZF2 - config/autoload/db.local.php

return array(

'db' => array(

'driver' => 'Pdo',

'dsn' => 'mysql:dbname=commentizr;host=localhost',

'driver_options' => array(

PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''

),

'username' => 'root',

'password' => '',

),

);

41 of 65

Zend_Db_Table

ZF1 - Application_Model_DbTable_Comment

class Application_Model_DbTable_Comment extends Zend_Db_Table_Abstract

{

protected $_name = 'comment';

}

42 of 65

Table Gateway

ZF2 - Module.php

public function getServiceConfig()

{

return array(

'factories' => array(

'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',

'Application\Model\CommentService' => function (ServiceManager $serviceManager) {

$commentTable = $serviceManager->get('Application\Model\CommentTable');

return new CommentService($commentTable);

},

'Application\Model\CommentTable' => function (ServiceManager $serviceManager) {

$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');

$hydrator = new ClassMethods();

$rowObjectPrototype = new Comment();

$resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);

$tableGateway = new TableGateway('comment', $dbAdapter, null, $resultSet);

return $tableGateway;

}

),

);

}

43 of 65

Table Gateway

ZF2 - Module.php

public function getServiceConfig()

{

return array(

'factories' => array(

'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',

'Application\Model\CommentService' => function (ServiceManager $serviceManager) {

$commentTable = $serviceManager->get('Application\Model\CommentTable');

return new CommentService($commentTable);

},

'Application\Model\CommentTable' => function (ServiceManager $serviceManager) {

$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');

$hydrator = new ClassMethods();

$rowObjectPrototype = new Comment();

$resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);

$tableGateway = new TableGateway('comment', $dbAdapter, null, $resultSet);

return $tableGateway;

}

),

);

}

44 of 65

The Hydration

ZF1 - Application_Model_CommentService

public function fetchAll($referer = null, $limit = 10)

{

if(is_null($referer)) {

$referer = Zend_Registry::get('Referer');

}

$resultSet = $this->commentTable

->select()

->where('referer = ?', $referer)->order('created DESC')->limit((int)$limit)

->query()->fetchAll();

$return = array();

foreach ($resultSet as $result) {

$comment = new Application_Model_Comment();

$comment->setId($result['id']);

$comment->setName($result['name']);

$comment->setComment($result['comment']);

$comment->setCreated($result['created']);

$comment->setEmail($result['email']);

$comment->getReferer($result['referer']);

$return[] = $comment;

}

return $return;

}

45 of 65

Table Gateway

ZF2 - Module.php

public function getServiceConfig()

{

return array(

'factories' => array(

'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',

'Application\Model\CommentService' => function (ServiceManager $serviceManager) {

$commentTable = $serviceManager->get('Application\Model\CommentTable');

return new CommentService($commentTable);

},

'Application\Model\CommentTable' => function (ServiceManager $serviceManager) {

$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');

$hydrator = new ClassMethods();

$rowObjectPrototype = new Comment();

$resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);

$tableGateway = new TableGateway('comment', $dbAdapter, null, $resultSet);

return $tableGateway;

}

),

);

}

46 of 65

Table Gateway

ZF2 - Module.php

public function getServiceConfig()

{

return array(

'factories' => array(

'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',

'Application\Model\CommentService' => function (ServiceManager $serviceManager) {

$commentTable = $serviceManager->get('Application\Model\CommentTable');

return new CommentService($commentTable);

},

'Application\Model\CommentTable' => function (ServiceManager $serviceManager) {

$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');

$hydrator = new ClassMethods();

$rowObjectPrototype = new Comment();

$resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);

$tableGateway = new TableGateway('comment', $dbAdapter, null, $resultSet);

return $tableGateway;

}

),

);

}

47 of 65

The Service

Update the CommentService to take dependency injected:

ZF2 - Application\Model\CommentService

namespace Application\Model;

use Zend\Db\TableGateway\TableGateway;

class CommentService

{

function __construct(TableGateway $commentTable)

{

(we did this already earlier)

48 of 65

The Service

Update the CommentService because it no longer needs to hydrate an object:

ZF2 - Application\Model\CommentService

public function fetchAll($referer = null, $limit = 10)

{

if (is_null($referer)) {

$referer = $this->referer;

}

$select = new Select('comment');

$select->where(array('referer' => $referer));

$select->order('created DESC');

$select->limit($limit);

$return = $this->commentTable->selectWith($select);

return $return;

}

(should be fairly familiar)

49 of 65

The Service

ZF1 - Application_Model_CommentService

public function save(Application_Model_Comment $comment)

{

$data = array(

'name' => $comment->getName(),

'email' => $comment->getEmail(),

'comment' => $comment->getComment(),

'created' => new Zend_Db_Expr('NOW()'),

'referer' => Zend_Registry::get('Referer')

);

if (is_null($comment->getId())) {

return $this->commentTable->insert($data);

}

return $this->commentTable->update($data, array('id = ?'), $comment->getComment());

}

50 of 65

The Service

ZF2 - Application\Model\CommentService

public function save(Comment $comment)

{

$data = array(

'name' => $comment->getName(),

'email' => $comment->getEmail(),

'comment' => $comment->getComment(),

'created' => new Expression('NOW()'),

'referer' => $this->referer

);

if (is_null($comment->getId())) {

return $this->commentTable->insert($data);

}

return $this->commentTable->update($data, array('id' => $comment->getComment()));

}

(we could use hydrator’s “extract” method here)

51 of 65

The Controller

ZF2 - module/Application/config/module.config.php

delete

'controllers' => array(

'invokables' => array(

'Application\Controller\Index' => 'Application\Controller\Index'

),

),

52 of 65

The Controller

ZF2 - Module.php

add

public function getControllerConfig()

{

return array('factories' => array(

'Application\Controller\Index' => function (ControllerManager $controllerManager) {

$commentService = $controllerManager

->getServiceLocator()->get('Application\Model\CommentService');

return new IndexController($commentService);

}

));

}

(We want to inject CommentService into the controller)

53 of 65

The Controller

ZF2 - Application\Controller\IndexController.php

class IndexController extends AbstractActionController

{

protected $commentService;

function __construct($commentService) {

$this->commentService = $commentService;

}

public function indexAction() {

$form = new CommentForm();

if ($this->getRequest()->isPost()) {

$form->setData($this->getRequest()->getPost());

if($form->isValid()) {

$this->commentService->save($form->getObject());

$this->redirect('/?referer=' . $this->commentService->getReferer());

}

} else {

$form->bind(new \Application\Model\Comment());

}

$form->get('referer')->setValue($this->commentService->getReferer());

$comments = $this->commentService->fetchAll();

return new ViewModel(array('form' => $form, 'comments' => $comments));

}

}

54 of 65

The View

Copy

views/scripts/index/index.phtml

to

module/Application/view/application/index

(view script locations are similar and can be defined)

55 of 65

The View

Edit

module/Application/view/application/index/index.php

$this->escape() => $this->escapeHtml()

$form->setAction() => $form->setAttribute('action', ...

echoing the form no longer works as there are no decorators ;)

56 of 65

The View

Form View Helpers:

<?php

$form->setAttribute('action', $this->url());

$form->prepare();

echo $this->form()->openTag($form);

echo $this->formCollection($form);

echo $this->form()->closeTag();

?>

57 of 65

The Front Controller Plugin

ZF1 - Application_Plugin_CallerPlugin

class Application_Plugin_CallerPlugin extends Zend_Controller_Plugin_Abstract

{

public function preDispatch(Zend_Controller_Request_Abstract $request)

{

if(is_null($request->getServer('HTTP_REFERER'))

&& is_null($request->getParam('referer'))) {

throw new Exception('REFERER is not set!');

}

if(!is_null($request->getParam('referer'))) {

$referer = $request->getParam('referer');

} else {

$referer = $request->getServer('HTTP_REFERER');

}

Zend_Registry::set('Referer', $referer);

}

}

(don’t do this)

58 of 65

The Event Manager

ZF2 - Module.php

public function onBootstrap(MvcEvent $event)

{

$request = $event->getRequest();

if (is_null($request->getServer('HTTP_REFERER')) && is_null($request->getPost('referer'))

&& is_null($request->getQuery('referer'))

) {

throw new \Exception('REFERER is not set!');

}

$commentService = $event->getApplication()->getServiceManager()->get('Application\Model\CommentService');

if(!is_null($request->getPost('referer'))) {

$commentService->setReferer($request->getPost('referer'));

return true;

}

if(!is_null($request->getQuery('referer'))) {

$commentService->setReferer($request->getQuery('referer'));

return true;

}

if(!is_null($request->getServer('HTTP_REFERER'))) {

$commentService->setReferer($request->getServer('HTTP_REFERER'));

return true;

}

}

(do do this)

59 of 65

The View Helper

ZF1 - Zend_View_Helper_DayDifference

public function DayDifference(Zend_Date $date1, Zend_Date $date2 = null)

{

$date1 = new DateTime($date1->get(Zend_Date::ISO_8601));

if ($date2 === null) {

$date2 = new Zend_Date();

}

$date2 = new DateTime($date2->get(Zend_Date::ISO_8601));

$diff = $date1->diff($date2);

...

if($diff->h > 0) {

return ($diff->h === 1) ? $diff->h . ' hour ago' : $diff->h . ' hours ago';

}

if($diff->i > 0) {

return ($diff->i === 1) ? $diff->i . ' minute ago' : $diff->i . ' minutes ago';

}

if($diff->s > 0) {

return ($diff->s === 1) ? $diff->s . ' second ago' : $diff->s . ' seconds ago';

}

return 'Right now';

}

60 of 65

The View Helper

ZF2 - Application\View\Helper\DayDifference

public function __invoke(\DateTime $date1, \DateTime $date2 = null)

{

if ($date2 === null) {

$date2 = new \DateTime();

}

$diff = $date1->diff($date2);

....

if ($diff->d > 0) {

return ($diff->d === 1) ? $diff->d . ' day ago' : $diff->h . ' days ago';

}

if($diff->h > 0) {

return ($diff->h === 1) ? $diff->h . ' hour ago' : $diff->h . ' hours ago';

}

if($diff->i > 0) {

return ($diff->i === 1) ? $diff->i . ' minute ago' : $diff->i . ' minutes ago';

}

if($diff->s > 0) {

return ($diff->s === 1) ? $diff->s . ' second ago' : $diff->s . ' seconds ago';

}

return 'Right now';

}

61 of 65

The View Helper

Now we tell the ViewHelperManager how to load the view helper:

ZF2 - Module.php

public function getViewHelperConfig()

{

return array(

'invokables' => array(

'DayDifference' => 'Application\View\Helper\DayDifference',

),

);

}

(you really should know this pattern by now)

62 of 65

It Works!

(my wonderful family)

63 of 65

It Doesn’t Work!

64 of 65

Gary Hockin

@GeeH on Twitter

GeeH on IRC (Freenode #roave)

gary@hock.in

https://github.com/Spabby/zendcon-zf1

https://github.com/Spabby/zendcon-zf2

QUESTIONS?

65 of 65

RATE ME OR ELSE!!!

https://joind.in/10862

http://whatculture.com/film/10-movie-villains-who-were-more-far-interesting-than-the-heroes.php