Using middleware in Zend MVC

Posted on 12th May 2017 | View comments


For over a year, since 2.7, we have had the ability to use a single middleware in a Zend\Mvc-based application (if you're not familiar, that's basically "Zend Framework" as opposed to "Zend Expressive"). A nice advantage here is you can make a forward-port path to migrating across to Zend Expressive. Your config might look something like

<?php
declare(strict_types=1);

return [
    'router' => [
        'routes' => [
            'path' => [
                'type' => 'Literal',
                'options' => [
                    'route' => '/path',
                    'defaults' => [
                        'middleware' => \App\Actions\FooAction::class,
                    ],
                ],
            ],
        ],
    ],
];

The middleware FooAction would be a standard middleware (at that time, the http-interop style middlewares were not supported, but that's changed now as I'll explain):

<?php
declare(strict_types=1);

namespace App\Actions;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse;

final class FooAction
{
    public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next = null) {
        return new JsonResponse([
            // ... some data ...
        ]);
    }
}

This change was great, and I did some work for a client who had started using the MiddlewareListener to attach a middleware to a route like this, and saw it in action. Over time I discovered a couple of flaws. Firstly, the matched route information was unavailable, so if you had a route matcher like /foo/:slug, there was no nice way to discover the value of slug from the matched route. So, dutifully I created patch #210 to resolve this issue, which was accepted and released in Zend\Mvc 3.0.4 and up.

Before long, I discovered another glaringly obvious problem: you could only ever have a single middleware added here. If you're familiar with middleware-style applications, this completely defeats the point: you can pipe your middleware to inject behaviour (such as authentication, error handling etc.) but this was not possible.

So, leveraging the existing functionality of Zend\Stratigility pipes, I heavily refactored the MiddlewareListener to instead create a pipe from the middleware definition from config. I made it backwards compatible in the sense that the configuration above would still work, but now you can attach multiple middlewares to a route, like so:

<?php
declare(strict_types=1);

return [
    'router' => [
        'routes' => [
            'path' => [
                'type' => 'Literal',
                'options' => [
                    'route' => '/path',
                    'defaults' => [
                        'middleware' => [
                            \App\Middleware\ExtractJsonPayload::class,
                            \App\Middleware\StoragelessPsr7Session::class,
                            \App\Middleware\VerifyIdentity::class,
                            \App\Actions\FooAction::class,
                        ],
                    ],
                ],
            ],
        ],
    ],
];

This patch recently got merged in Zend\Mvc 3.1.0 so you can now take advantage of this handy new feature. I also added support for middlewares that implement Interop\Http\ServerMiddleware\MiddlewareInterface too, so you can now write something like:

<?php
declare(strict_types=1);

namespace App\Actions;

use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\JsonResponse;

final class FooAction implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
    {
        return new JsonResponse([
            // ... some data ...
        ]);
    }
}

I provide development, consulting and training for Zend Expressive, Zend Framework, Doctrine and more. If you'd like to discuss your requirements, get in touch with me.

Zend Expressive + Doctrine integration - now even easier!

Posted on 4th March 2016 | View comments


Introduction

Zend Expressive is the new framework on the block, and it's super easy to get up and running with it. On many applications, before long, you'll need to start integrating with a database. My go-to solution, because I'm very familiar with it, is Doctrine. I've already written a guide to set up Doctrine into a Zend Expressive application, but now there's an even easier way of doing this, courtesy of the efforts of Ben Scholzen.

I'd like to introduce you to his new binding library, dasprid/container-interop-doctrine, and show you how to get started with this very simple-to-use set of factories.

Set up a Zend Expressive application

First up, we're going to start from absolute scratch with this project, so we'll start out with a blank canvas using the Zend Expressive skeleton application. As per the Expressive instructions, we simply have to:

$ composer create-project zendframework/zend-expressive-skeleton doctrine-test

When I set up, I accepted all the default options, except I did not opt to install Whoops error handler:

  • Full skeleton
  • FastRoute
  • Zend ServiceManager
  • No template engine
  • No error handler

Now we're set up, don't forget to descend into your new project directory for the rest of the commands.

$ cd doctrine-test

Install container-interop-doctrine

Because Ben is a neat guy, he put his library on Packagist, which makes it super easy to install with Composer:

$ composer require dasprid/container-interop-doctrine

Let's also do a quick sanity check, to ensure everything is working as we'd expect. Fire up a temporary PHP server using the following:

$ php -S 0.0.0.0:8080 -t public/ public/index.php

If you now open up http://localhost:8080/ in your browser, you should see some JSON output indicating everything works:

{"welcome":"Congratulations! You have installed the zend-expressive skeleton application.","docsUrl":"zend-expressive.readthedocs.org"}

If you see this, we're looking good, and can go on to start configuring and using container-interop-doctrine.

Configure container-interop-doctrine

First up we need some configuration, and you'll need an existing MySQL server (or other type of DB that Doctrine supports... take your pick!). I used MySQL, because it was already installed on my machine, so easy to set up. Create your Doctrine configuration in a sensibly named file - to keep things neatly organised, I recommend creating a new file in config/autoload/doctrine.local.php specifically for this purpose.

<?php
return [
    'doctrine' => [
        'connection' => [
            'orm_default' => [
                'params' => [
                    'url' => 'mysql://username:password@localhost/database',
                ],
            ],
        ],
        'driver' => [
            'orm_default' => [
                'class' => \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain::class,
                'drivers' => [
                    'App\Entity' => 'my_entity',
                ],
            ],
            'my_entity' => [
                'class' => \Doctrine\ORM\Mapping\Driver\AnnotationDriver::class,
                'cache' => 'array',
                'paths' => __DIR__ . '/../../src/App/Entity',
            ],
        ],
    ],
];

For your project, you may wish to also create a doctrine.local.php.dist containing a template or example of configuration for other users of your project.

Ben's library container-interop-doctrine has made the set up of the rest of the Doctrine factories super simple by requiring you to only register one single little factory! Head into your dependencies.global.php and add a register for the following factory:

<?php
return [
    'dependencies' => [
        'factories' => [
            'doctrine.entity_manager.orm_default' => \ContainerInteropDoctrine\EntityManagerFactory::class,
        ],
    ],
];

That's actually all the configuration needed to get this thing up and running! Now we can go ahead and make a very basic example to prove everything works as expected.

Create an entity

As you might've noticed in the configuration above, we've pointed our entity driver at the src/App/Entity folder, where we're going to create a very basic entity - just two fields, id and name. This is what my src/App/Entity/Foo.php looks like:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="foo")
 */
class Foo implements \JsonSerializable
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @var int
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", length=32)
     * @var string
     */
    private $name;

    /**
     * Application constructor.
     * @param $name
     */
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function jsonSerialize()
    {
        return [
            'id' => $this->id,
            'name' => $this->name
        ];
    }
}

I'm not going to mess around too much with the ORM stuff, for now, you can run this script manually to create the appropriate database table.

CREATE TABLE `foo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `foo` (`id`, `name`) VALUES
  (1, 'Testing'),
  (2, 'Testing2');

Naturally, if you're using a different flavour, storage engine, ODM etc., you'll need to adapt this accordingly - this is left as an exercise for the reader.

Update the HomePageAction and Factory

I'm going to go ahead and hack up the src/App/Action/HomePageAction.php now so that it has the pre-configured EntityManager injected and ready to go:

<?php

namespace App\Action;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\JsonResponse;
use Doctrine\ORM\EntityManager;
use App\Entity\Foo;

class HomePageAction
{
    private $entityManager;

    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
    {
        return new JsonResponse($this->entityManager->find(Foo::class, 1));
    }
}

And the factory, in src/App/Action/HomePageFactory.php needs updating to only inject the Entity Manager:

<?php

namespace App\Action;

use Interop\Container\ContainerInterface;

class HomePageFactory
{
    public function __invoke(ContainerInterface $container)
    {
        $em = $container->get('doctrine.entity_manager.orm_default');

        return new HomePageAction($em);
    }
}

Naturally, this is not something I'd do in production, but this is just a demo, and the quickest way to prove the concept. If you're interested, I'd wrap this fetch process into a service class to abstract the Doctrine logic away from your controller action, so that it can work with a consistent internal API, rather than external dependencies.

That's it! Check it and see it work...

Head back to the browser at http://localhost:8080/ and you should see your new updated JSON response:

{"id":1,"name":"Testing"}

Change the requested ID in the HomePageAction to 2 and you'll see the response change accordingly.

{"id":2,"name":"Testing2"}

As you can see, setting up and binding the Doctrine library to a new Zend Expressive application has been made trivially easy - and it probably takes about 10 minutes to set this all up. For a bare-bones, quick and easy, set up this is really ideal in my opinion, and I'd encourage you to look into this route. That said, there's still times when the functionality that the DoctrineORMModule is a necessity - for example the ObjectSelect and hydrators for binding to Zend\Form instances and so on. If that's something you think you may need, then you could use the above as a basis, but additionally include DoctrineORMModule for the functionality you require.

P.S., CLI tools?

One of the powerful features of Doctrine is the CLI tools that allow you to manage schema, create migrations and so on. For this, Ben provides instructions on setting up the CLI tools over on the README.md - which is something you'll probably want to set up too to take advantage.

Integrating Doctrine 2 ORM and Migrations into Zend Expressive

Posted on 11th December 2015 | View comments


I've started a new project in which we're looking at using Doctrine 2 ORM, but we're in a Zend Expressive project. As of yet, Expressive is in it's infancy, which means there are still things undocumented and missing. So I'm putting my two cents into how we got the ORM working in our project.

First up, we're going to use DoctrineORMModule -
we're mostly using ZF components (such as Zend\Form) so it makes sense to use something that already integrates with this later down the line, so start with

$ composer require doctrine/doctrine-orm-module
$ composer require doctrine/migrations

If you don't want to use the ORM, ignore the instructions for ORM, and the same for Migrations.

Next up, you want your configuration to be included. This would be easy if it were not for the fact Zend Expressive looks for the dependencies config key, but Doctrine has this all in service_manager. So we need to make a shim which reads the config of both DoctrineModule and DoctrineORMModule and merge them into the final config:

<?php

use Zend\Stdlib\ArrayUtils;

$vendorPath = __DIR__ . '/../../vendor'; // You may need to adjust this depending on your structure...

$doctrineModuleConfig = require_once $vendorPath . '/doctrine/doctrine-module/config/module.config.php';
$doctrineModuleConfig['dependencies'] = $doctrineModuleConfig['service_manager'];
unset($doctrineModuleConfig['service_manager']);

$doctrineOrmModuleConfig = require_once $vendorPath . '/doctrine/doctrine-orm-module/config/module.config.php';
$doctrineOrmModuleConfig['dependencies'] = $doctrineOrmModuleConfig['service_manager'];
unset($doctrineOrmModuleConfig['service_manager']);

return ArrayUtils::merge($doctrineModuleConfig, $doctrineOrmModuleConfig);

Once this is done, there's a couple more things to do. First we need to provide our own Doctrine connection / driver / migrations configurations. This is the normal setup, so check out the DoctrineORMModule docs for this. We simply placed our config into doctrine.global.php with the default configuration, and then specify platform-specific config into a Git-ignored local.php.

Finally, because we're using the ORM with annotations, we need to register the annotation loader:

\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(
    function ($className) {
        return class_exists($className);
    }
);

This code can actually live anywhere, but we added a Bootstrap middleware which has a function that calls this. This could be seen as synonymous with a Module.php in ZF2's bootstrap sequence.

If anyone has any feedback, or simpler ways of doing this, I'd love to hear :)

EDIT There was one more thing, in your dependencies.global.php you need to alias configuration to config, because it looks for configuration:

<?php

return [
    'dependencies' => [
        'aliases' => [
            'configuration' => 'config',
        ],
    ],
];

Testing with ZF2 Controller Plugins

Posted on 20th May 2015 | View comments


One of the things I've found that can make controllers difficult to test in Zend Framework 2 is the use of controller plugins. Although it makes the functionality provided by the plugin itself easier to test, I found it makes it difficult to test the controller itself. In the past I've resorted to mocking the controller and in turn mocking the controller plugin as if it was a method.

However, I've looked into this a bit more and it looks like there's a better way. You can simply use the controller's controller plugin manager, and pop the required service in (i.e. the controller plugin - could be a mock or the actual plugin itself, depending on your needs). This simplifies testing a controller that uses plugins greatly. Consider this controller:

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class MyController extends AbstractActionController
{
    public function indexAction()
    {
        $foo = $this->foo(); // is a controller plugin that returns something

        return ['foo' => $foo];
    }
}

And our controller plugin:

<?php

namespace Application\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

class Foo extends AbstractPlugin
{
    public function __invoke()
    {
        return "Woo hello!";
    }
}

And then to write our test for this, we could do the following:

<?php

namespace ApplicationTest\Controller;

use Application\Controller\MyController;
use Application\Controller\Plugin\Foo;

class MyControllerTest extends \PHPUnit_Framework_TestCase
{
    public function testIndexActionReturnsFoo()
    {
        $fooMock = $this->getMockBuilder(Foo::class)
            ->setMethods(['__invoke'])
            ->getMock();

        $fooMock->expects($this->once())->method('__invoke')->will($this->returnValue('Hello world!'));

        $controller = new MyController();
        $controller->getPluginManager()->setService('foo', $fooMock);

        $returnValue = $controller->indexAction();

        $this->assertSame(['foo' => 'Hello world!'], $returnValue);
    }
}

You could substitute the mock in the example above with a real instance of the controller plugin without too much hassle.

I set up a working demo project on GitHub which you can clone here.

ZF2 + Doctrine ObjectSelect + Form binding + Custom Select Options

Posted on 27th September 2013 | View comments


This morning me and another developer have spent 3.5 hours figuring out how on earth to solve our problem. We are writing a Zend Framework 2 application that uses Doctrine entities. The trouble was figuring out how to use the $form->bind(..) method with our Doctrine entities. Turns out, a combination of RTFM and sifting through the code for Doctrine, what we were trying to do is possible without nasty hacks. In the examples below, the AdVariant entity is the entity we are creating the edit form for, and in our application's vernacular, a "module" is a semantic "grouping" of pages - it does not mean a ZF2 "Module"!

Step 1 - Hydration

First, we need to use Doctrine's built in Hydrator (what is a hydrator?) in our form:

<?php
namespace Ed\Advertisers\Form;

// .. more use
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;

class AdVariantForm extends Form implements InputFilterProviderInterface
{
    protected $entityManager;
    protected $moduleService;
    protected $pageService;

    public function __construct(EntityManager $entityManager, ModuleService $moduleService, PageService $pageService, $name = null)
    {
        parent::__construct($name);

        $hydrator = new DoctrineHydrator($entityManager, '\Ed\Advertisers\Entity\AdVariant');
        $this->setHydrator($hydrator);

        // ... set up elements here
    }
}

The DoctrineHydrator basically tells Zend\Form:

  • ... how to populate the form when given an entity (extract)
  • ... how to populate an entity when given a form (hydrate)

So in the code snippet, we create the DoctrineHydrator, give it the object manager and tell it what Entity we are using for hydration.

Step 2 - Bind Entity to Form

This is pretty much as per the example given in the Doctrine Hydrator documentation above:

public function editAction()
{
    // .. create the form
    // .. load $adVariant from query string params

    $form->bind($adVariant);

    if ($this->getRequest()->isPost())
    {
        $form->setData($this->getRequest()->getPost());

        if ($form->isValid())
        {
            $this->adVariantService->getEntityManager()->persist($adVariant);
            $this->adVariantService->getEntityManager()->flush();

            // .. redirect or whatever else
        }
    }

    // .. other stuff
}

Previously, we were populating the form using:

$form->setData($entity->getArrayCopy())

and then populating the entity by fetching values from the validated form and using the entity's setters, which was pretty ugly:

$adGroupId = $form->get('adGroupId')->getValue();
$adGroup = $this->adGroupService->fetchById($adGroupId);

// .. fetch other entities in this awful way

$name = $form->get('name')->getValue();
// .. other entitites

// .. using lots of setters to populate!!
$adVariant->setName($name);
$adVariant->setAdGroup($adGroup);
$adVariant->setWebsite($website);
$adVariant->setProduct($product);
$adVariant->setBusiness($business);
$adVariant->setLandingPage($landingPage);
$adVariant->setTargetedLandingPage($targetedLandingPage);
$adVariant->setTrackingCode($trackingCode);
$adVariant->setCostType($costType);
$adVariant->setUnitCost($unitCost);
$adVariant->setStatus($status);
$adVariant->setDateUpdated(new \DateTime());

Step 3 - Using Doctrine ObjectSelect

Instead of using ZF2's Select element, we should use Doctrine's ObjectSelect from its form element collection, which works with a "Proxy" object. The ObjectSelect itself is pretty straightforward. Here's how to use it:

$adGroup = new ObjectSelect('adGroup');
$adGroup->setEmptyOption('Select..');
$adGroup->setOptions(array(
    'object_manager' => $this->entityManager,
    'target_class' => '\Ed\Advertisers\Entity\AdGroup',
    'property' => 'name',
    'is_method' => true,
    'find_method' => array(
        'name' => 'findBy',
        'params' => array(
            'criteria' => array('status' => 'ACTIVE'),
        ),
    ),
));

This uses the repository's findBy() method to find all Ad Groups with a status of "ACTIVE".

Step 4 - Custom options

After examining the ObjectSelect class, it seems we can provide our own options (for example, we wanted to have <optgroup> groups on one of our dropdowns):

// .. build $optionValues, e.g.:
// $optionValues = array(
//   0 => array(
//     'label' => 'MyModule1',
//     'options' => array(
//       1 => 'page1',
//       2 => 'page2',
//       3 => 'page3',
//     ),
//   ),
//   1 => array(
//     'label' => 'MyModule2',
//     'options' => array(
//       4 => 'page1',
//       5 => 'page2',
//       6 => 'page3',
//     ),
//   ),
// );
$landingPage = new ObjectSelect('landingPage');
$landingPage->setEmptyOption('Select..');
$landingPage->setOptions(array(
    'object_manager' => $this->entityManager,
    'target_class' => '\Ed\Advertisers\Entity\AdGroup',
));
$landingPage->setValueOptions($optionValues);

This works because in the ObjectSelect->getValueOptions() method only calls the proxy method if the $this->valueOptions is NOT empty:

public function getValueOptions()
{
    if (empty($this->valueOptions)) { // this won't be empty because we have called setValueOptions in our form
        $this->setValueOptions($this->getProxy()->getValueOptions());
    }
    return $this->valueOptions;
}

Huzzah! Things now work :)