Integrating BjyAuthorize with Zend\Navigation

19th November 2012

If you are using BjyAuthorize for ACL configuration and want to use Zend\Navigation's Zend\Acl integration features, then you need to set the Acl and Role information into Zend\Navigation.

The easiest way to do this is to add the following to Application\Module::onBoostrap():

        $sm = $e->getApplication()->getServiceManager();
 
        // Add ACL information to the Navigation view helper
        $authorize = $sm->get('BjyAuthorize\Service\Authorize');
        $acl = $authorize->getAcl();
        $role = $authorize->getIdentity();
        \Zend\View\Helper\Navigation::setDefaultAcl($acl);
        \Zend\View\Helper\Navigation::setDefaultRole($role);

This assumes that you've set up BjyAuthorize with some resources and rules. For example, in my config/autoload/bjyauthorize.global.php, I have a 'bug' resource and have a rule that allows the reporter role access to the list and add privileges:

        'resource_providers' => array(
            'BjyAuthorize\Provider\Resource\Config' => array(
                'bug' => array(),
            ),
        ),
 
        'rule_providers' => array(
            'BjyAuthorize\Provider\Rule\Config' => array(
                'allow' => array(
                    array(array('reporter'), 'bug', array('list', 'add')),
                ),
            ),
        ),

My Zend\Navigation configuration for the bug menu item is in my Bug module's module.config.php and it looks like:

    'navigation' => array(
        'site' => array(
            'bug' => array(
                'label' => 'Bugs',
                'route' => 'bug',
                'resource' => 'bug',
                'privilege' => 'list',
                'pages' => array(
                    'create' => array(
                        'label' => 'Create new project',
                        'route' => 'bug/create',
                        'resource' => 'bug',
                        'privilege' => 'add',
                    ),                        
                ),
            ),
        ),        
    ),

That's all there is to it.

Introducing AkrabatSession

15th November 2012

One of the requirements for a new app that I'm writing is that it has a specific session name. In Zend Framework 2, this is done by creating a SessionManager with the correct configuration and then setting the default manager on the Session Container:

use Zend\Session\Config\SessionConfig;
use Zend\Session\SessionManager;
use Zend\Session\Container;
 
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions(array('name'=>'MY_SESSION_NAME');
$sessionManager = new SessionManager($config);
Container::setDefaultManager($sessionManager);

Obviously, I need to be able to configure the name (and potentially other session configuration options) from my config/autoload/global.php file and this is a generically useful requirement, so I created the AkrabatSession module.

This is a really simple module that simply allows you to configure the SessionManager with minimal effort:

  1. Install AkrabatSession.
  2. Enable it as the first module in application.config.php
  3. Add the following to your configuration array in global.php:
        'session' => array(
            'name' => 'MY_SESSION_NAME_HERE',
        ),

Further details are in the README file and of course, it's available on Packagist.

Zend\ServiceManager configuration keys

12th November 2012

Zend\ServiceManager is usually configured in two places: an array in a config file or a method within your Module class. In either case, you provide a nested array of configuration information.

For example, in a config file:

return array(
    'service_manager' => array(
        'invokables' => array(
            'session' => 'Zend\Session\Storage\SessionStorage',
        ),
        'factories' => array(
            'db' => 'Zend\Db\Adapter\AdapterServiceFactory',
        ),
    )
);

Within the service_manager array, there are a set of nested arrays which are generally used to configure how you want a given class to be instantiated. the names of these sub-arrays are hardcoded, so you just need to learn their names and the difference between them:

invokables A string which is the name of a class to be instantiated. The ServiceManager will instantiate the class for you when needed. For example:

'invokables' => array(
    'zfcuser_user' => 'User\Service\User'
),
services An instance of a class. This is used to register already instantiated objects with the ServiceManager. For example:

'services' => array(
    'rob' => $rob,  // $rob is already instantiated 
),
factories A callback that will return an instantiated class. This is for cases where you need to configure the instance of the object. For example:

'factories' => array(
    'MyModule\Mapper\Comment' =>  function($sm) {
        $mapper = new \MyModule\Mapper\Comment();
        $db = $sm->get('Zend\Db\Adapter\Adapter');
        $mapper->setDbAdapter($db);
        return $mapper;
    },
),
aliases Another name for a class. Generally, you see this used within a module so that the module uses it's own alias name and then the user of the module can configure exactly which class that alias name is to be.
For example:

'aliases' => array(
    'mymodule_zend_db_adapter' => 'Zend\Db\Adapter\Adapter',
),
initializers A callback that is executed every time the ServiceManager creates a new instance of a class. These are usually used to inject an object into the new class instance if that class implements a particular interface.
For example:

'initializers' => array(
    function ($instance, $sm) {
        if ($instance instanceof AuthorizeAwareInterface) {
            $instance->setAuthorizeService($sm->get('auth_service'));
        }
    }
)

In this case, the initialiser checks if $instance implements AuthorizeAwareInterface and if it injects the Authorize service into the instance ready for use. Another really common use-case is injecting a database adapter and Zend Framework supplies Zend\Db\Adapter\AdapterAwareInterface for this case.

There is also the abstract_factories key, but this is rarely used in most apps.

abstract_factories A factory instance that can create multiple services based on the name supplied to the factory. This is used to enable ServiceManager to fallback to another Service Locator system if it can cannot locate the required class from within its own configuration. As an example, you could write an abstract factory that proxies to Symfony's DependencyInjection component. Items within this sub-key can be either a classname string or an instance of the factory itself For example:

array('abstract_factories' => array( 
    new DiStrictAbstractServiceFactory(),
);

All abstract factories must implement Zend\ServiceManager\AbstractFactoryInterface.

Controllers, View helpers & Controller plugins

Note that the MVC system instantiates controllers, view helpers and controller plugins using specialised versions of ServiceManager. This means that the same keys that you use for service manager configuration are used for setting up view helpers and controller plugins - you just use a different top level configuration key and method name the in Module class:

Manager Key name in configuration array Method name in Module.php
ServiceManager service_manager getServiceConfig()
ViewHelperManager view_helpers getViewHelperConfig()
ControllerPluginManager controller_plugins getControllerPluginConfig()
ControllerManager controllers getControllerConfig()

This is reuse of knowledge at its best!

Sending an HTML with text alternative email with Zend\Mail

2nd November 2012

Sending a multi-part email with Zend\Mail is easy enough, but if you want to send an HTML email with a text alternative, you need to remember to set the content-type in the headers to multipart/alternative. As this is the second time I had to work this out, I'm noting it here for the next time I forget!

use Zend\Mail;
use Zend\Mime\Message as MimeMessage;
use Zend\Mime\Part as MimePart;
 
 
function sendMail($htmlBody, $textBody, $subject, $from, $to)
{
    $htmlPart = new MimePart($htmlBody);
    $htmlPart->type = "text/html";
 
    $textPart = new MimePart($textBody);
    $textPart->type = "text/plain";
 
    $body = new MimeMessage();
    $body->setParts(array($textPart, $htmlPart));
 
    $message = new Mail\Message();
    $message->setFrom($from);
    $message->addTo($to);
    $message->setSubject($subject);
 
    $message->setEncoding("UTF-8");
    $message->setBody($body);
    $message->getHeaders()->get('content-type')->setType('multipart/alternative');
 
    $transport = new Mail\Transport\Sendmail();
    $transport->send($message);
}

Module specific layouts in ZF2

14th September 2012

If you need different layout scripts to be rendered for different modules in Zend Framework 2, then Evan Coury has made this extremely easy. His new module EdpModuleLayouts is just the ticket!

Once installed, you simply have to add a new array to a config file in the config/autoload folder with the following in it:

array(
    'module_layouts' => array(
        'Application' => 'layout/application',
        'ZfcUser' => 'layout/user',
    ),
);

i.e. you provide a list of the module name against the layout script to use.

What could be easier?