Learning ZF2: The Controller

by Mateusz Tymek — on PHP, Zend Framework

Head's up! This post was written back in 2012 and is very likely to contain outdated information.
Zend Framework 2

When Zend Framework 2 was officially released, I wanted to learn it on practical example. So, I decided to use it as a foundation for my new blog engine. Because it is very simple application (just a few classes), I was able to build it pretty easily.

This is first post of short series where I will describe how common problems are solved in ZF2. I will always compare it with similar code written in ZF1-style.

First I'm going to describe some common tasks that you usually do in your controllers: handling input parameters and accessing application services.

Parameters

There are few different types of parameters that you may want to access from controller:

  • route parameters (defined in router config)
  • query parameters (what you'd normally access via $_GET)
  • post parameters ($_POST)

Before getting to actual code, let's assume we have following route defined:

/view-album/:artist/:album_name

And that user has following in his address bar:

/view/album/Nirvana/Nevermind?view_details=1

In Zend Framework 1 all parameters listed above are available through Zend_Controller_Request_Http class. Here's how it can be used from controller:

$request = $this->getRequest();

// route parameters
$artist = $request->getParam('artist');
$albumName = $request->getParam('album_name');

// query parameter
$viewDetails = $request->getParam('view_details');

// post parameter
$viewDetails = $request->getParam('comment');

// or, in explicit way:
$viewDetails = $request->getPost('view_details');

In Zend Framework 2 you have to be more explicit on what you want to retrieve. Request doesn't let you retrieve route parameters anymore (route params are part of router logic, they are not defined in HTTP request) - you need to use RouteMatch object.
Code below shows how to access this data in ZF2:

$request = $this->getRequest();
$routeMatch = $this->getRouteMatch();

// route parameters
$artist = $routeMatch->getParam('artist');
$albumName = $routeMatch->getParam('album_name');

// query parameter
$viewDetails = $request->getQuery('view_details');

// post parameter
$viewDetails = $request->getPost('comment');

Accessing dependencies

Zend Framework 1 didn't come with decent dependency system. Different programmers used different solutions to manage relations between objects:

public function indexAction()
{
    // some objects will take care of their dependencies by themselves
    $userService = new App_Service_User();

    // sometimes services were pulled from Bootstrap object...
    $db = $this->getInvokeArg('bootstrap')->getResource('db');

    // ... or from registry:
    $translator = Zend_Registry::get('Zend_Translate');
}

Zend Framework 2 comes with ServiceManager - dedicated class that takes care of service creation and serves as object container. The thing that makes it different from registry or Zend Bootstrap is that it creates objects only when they are needed. It brings significant improvement to loading speed - you don't have to initialize mail transport and database only to display static page.

Let's see an example of getting objects from ServiceManager:

public function indexAction()
{
    $locator = $this->getServiceLocator();

    $userService = $locator->get('UserService');

    $db = $locator->get('DatabaseAdapter');

    $translator = $locator->get('Translator');
}

Wait... what's different in this example, besides different function names? If you are in doubt, then you're right: it's not how you should write controller code in 2012.

Zend's User Guide gives you good example on how it should look like. Here's my code:

/**
 * @var UserService
 */
protected $userService;

public function setUserService(UserService $service)
{
    $this->userService = $service;
}

public function getUserService()
{
    if (!$this->userService) {
        $locator = $this->getServiceLocator();
        $this->userService = $locator->get('UserService');
    }
    return $this->userService;
}

public function indexAction()
{
    $userService = $this->getUserService();

    // ...
}

What's the advantage of crafting controllers like in above example?

  • services are created only when they are required
  • it is easy to create unit tests for controller, when you can provide mock objects using setter method
  • your IDE will now help you with auto completion

Finally, some IDEs can generate most of this extra code for you, meaning you don't have to type all that manually.


comments powered by Disqus