Zend Framework 2 config and classmaps

Binnen Zend Framework 2 werk je met heel veel config files die via autoloaders worden geladen. Dit maakt het bijzonder prettig modulair werken maar geeft in productie snel een flinke load. Nu kun je eenvoudig de caching hiervan configureren voor betere performance in productie.

Application.config.php

Ik heb ervoor gekozen te werken met een application_env (zoals ik dat gewend was in Zend Framework 1) om environment gebonden configuraties toe te passen. Globaal ziet mijn config er zo uit (als het gaat om caching en de environment);

$env = getenv('APPLICATION_ENV') ?: 'production';

$array = array(
    'modules' => array(
        'Application',
    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,' . $env . '}.php',
        ),
        'cache_dir' => __DIR__ . '/../data/cache/modulecache',
        'config_cache_enabled' =>  true,
        'config_cache_key' =>  $env,
        'module_map_cache_enabled' => true,
        'module_map_cache_key' => $env,
    ),
);

if('development' == $env) {
    $array['module_listener_options']['module_map_cache_enabled'] = false;
    $array['module_listener_options']['config_cache_enabled'] = false;
    array_unshift($array['modules'], 'Whoops');
    array_push($array['modules'], 'BjyProfiler', 'ZendDeveloperTools');
}

return $array;

Dat laatste stukje is beetje een iets minder mooie maar effectieve oplossing om mijn development-omgeving gebonden tools (ZendDeveloperTools, BjyProfiler & Whoops) te laden en de caching, die by default op true staat, uit te zetten. In acceptatie, productie en welke environment dan ook werkt alles wel conform de reguliere config. Dus caching aan en een enviroment-based key.

Autoload_classmap.php

Wel een leuke performance toevoeging. Classmaps die met autoload meekomen zodat Zend niet alles overal hoeft te zoeken. Je kunt ze eenvoudig via de commandline genereren met de, met ZF2 meegeleverde, classmap_generator.php. Deze staat uiteindelijk in de root van je module dus bijv. in dit geval /task/autoload_classmap.php

In je Module.php zet je dan het volgende neer om deze classmap te laden;

    /**
     * @return array
     */
    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\ClassMapAutoloader' => array(
                __DIR__ . '/autoload_classmap.php',
            ),
        );
    }

De autoload_classmap.php ziet er ongeveer zo uit (hangt er af van je module, in dit geval ‘task’);

// Generated by ZF2's ./bin/classmap_generator.php
return array(
  'Task\Task'                                   => __DIR__ . '/Task.php',
  'Task\Controller\TaskAdminController'       => __DIR__ . '/src/Task/Controller/TaskAdminController.php',
  'Task\Entity\TaskAdminEntity'               => __DIR__ . '/src/Task/Entity/TaskAdminEntity.php',
  'Task\Form\TaskAdminForm'                   => __DIR__ . '/src/Task/Form/TaskAdminForm.php',
  'Task\InputFilter\TaskInputFilter'          => __DIR__ . '/src/Task/InputFilter/TaskInputFilter.php',
  'Task\Model\TaskAdminModel'                 => __DIR__ . '/src/Task/Model/TaskAdminModel.php',
  'Task\Service\TaskAdminService'             => __DIR__ . '/src/Task/Service/TaskAdminService.php',
);

Zend Framework 2: Form validation error messages translated

Om mij nog niet geheel duidelijke redenen gebruikt ZF2 voor de vertalingen van validatie errors niet de default locale. Dit is erg spijtig maar is relatief eenvoudig op te lossen. Even de stappen bij elkaar om het helder te houden.

Translator configureren: autoload/global.php

'service_manager' => array(
    'aliases' => array(
        'translator' => 'MvcTranslator',
    ),
),
'translator' => array(
    'locale' => 'nl_NL',
    'translation_file_patterns' => array(
        array(
            'type' => 'phpArray',
            'base_dir' => __DIR__ . '/../../resources/languages',
            'pattern' => '/%s/Zend_Validate.php',
        ),
    ),
),

Hierbij zorgt regel 7 ( ‘locale’ => ‘nl_NL’) ervoor dat de default locale van de gehele applicatie op nl_NL komt te staan. Uiteraard kan dit ook via bijvoorbeeld een call in de bootstrap gedaan worden op basis van taalinstellingen browser, url of wat voor leuks dan ook.

Forceren validation error translation: module.php

Dan de verassing; de vertalingen van de validatie errrors zijn niet vertaald. Om dit te forceren moet je in de bootstrap van de module het volgende toevoegen;

public function onBootstrap($e)
{
    $translator = $e->getApplication()->getServiceManager()->get('translator');
    AbstractValidator::setDefaultTranslator($translator);
}

Met een beetje geluk zijn nu de validatie errors van de forms netjes vertaald naar de Nederlandse versie.

Zend Framework 2 controller based layout

In Zend Framework 2 kan het soms wenselijk zijn een afwijkende layout te configureren voor een controller. Bijvoorbeeld om een admin & front controller te dienen met een afwijkende layout. Er zijn hiervoor een aantal voorbeelden te vinden maar was zelf niet helemaal tevreden hierover dus kwam uiteindelijk met het volgende om een verschil te maken tussen de PageAdminController.php & PageFrontController.php

module.config.php


return array(
    'view_manager' => array(
        'template_map' => array(
            'layout/layout' => __DIR__ . '/../view/layout/default.phtml',
            'layout/admin' => __DIR__ . '/../view/layout/admin.phtml',
            'layout/front' => __DIR__ . '/../view/layout/front.phtml',
        ),
    ),
);

module.php


public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();
    $eventManager->getSharedManager()->attach(
        'Zend\Mvc\Controller\AbstractActionController',
        'dispatch',
        function ($e) {
            $controller = $e->getTarget();
            $ccString = preg_split('/(?=[A-Z])/', get_class($controller));
            $layoutType = strtolower($ccString[count($ccString) - 2]);
            $config = $e->getApplication()->getServiceManager()->get('config');
            if (isset($config['view_manager']['template_map']['layout/' . $layoutType])) {
                $controller->layout('layout/' . $layoutType);
            }
        }
    );
}

Wat hij nu doet is in de module.php op basis van de controller-naam controleren of er een layout gedefinieerd is. Zo niet dan doet hij niets en gebruikt hij de default (layout/layout). Bestaat hij wel dan gebruikt hij bijvoorbeeld de layout/admin of layout/front.