Welcome to Abdul Malik Ikhsan's Blog

Compose modular Expressive application with “ZF2 style” structure

Posted in Tutorial PHP, Zend Framework by samsonasik on December 28, 2015

expressive-modular-structure-strategy Yes, there is a doc for compose modular application in Zend\Expressive. And No, I won’t debate about how non-shareable module shouldn’t be in other modules (only “root” module), which is on personal preference. In this post, I just want to show you how to compose modular application like the “ZF2” structure, with config, view, and class that require the config per-“module”, and that’s do-able!.

Let’s do it!:

1) Same as doc, requiring mtymek/expressive-config-manager:

$ composer require mtymek/expressive-config-manager

2) Register your modules as namespace in composer autoload:

// composer.json
// ...
    "autoload": {
        "psr-4": {
            "App\\": "src/App/",
            "Album\\": "src/Album/"
        }
    },
// ...

3) Run dump-autoload

$ composer dump-autoload

4) Let’s extract one by one per-module, “App” and “Album”
a. src/App/AppConfig.php

//src/App/AppConfig.php
namespace App;

class AppConfig
{
    public function __invoke()
    {
        return include __DIR__ . '/config/expressive-config.php';
    }
}

b. src/Album/AlbumConfig.php

//src/Album/AlbumConfig.php
namespace Album;

class AlbumConfig
{
    public function __invoke()
    {
        return include __DIR__ . '/config/expressive-config.php';
    }
}

c. src/App/config/expressive-config.php
You can move various arrays from config/autoload/routes.global.php to src/App/config/expressive-config.php:

return [
    
    'dependencies' => [
        'invokables' => [
            App\Action\PingAction::class => App\Action\PingAction::class,
        ],
        'factories' => [
            App\Action\HomePageAction::class => App\Action\HomePageFactory::class,
        ],
    ],
    
    'routes' => [
        [
            'name' => 'home',
            'path' => '/',
            'middleware' => App\Action\HomePageAction::class,
            'allowed_methods' => ['GET'],
        ],
        [
            'name' => 'api.ping',
            'path' => '/api/ping',
            'middleware' => App\Action\PingAction::class,
            'allowed_methods' => ['GET'],
        ],
    ],
];

d. src/Album/config/expressive-config.php

Like App config, you can define:

return [
    
    'dependencies' => [
        'factories' => [
            Album\Action\AlbumPageAction::class => Album\Action\AlbumPageFactory::class,
        ],
    ],
    
    'routes' => [
        [
            'name' => 'album',
            'path' => '/album',
            'middleware' => Album\Action\AlbumPageAction::class,
            'allowed_methods' => ['GET'],
        ],
    ],
];

5) Templates
As in the “App” and “Album” modules, at this case, we need template, we can define templates path in the config:

a. for App config

// src/App/config/expressive-config.php
return [
    // ...
    'templates' => [
        'paths' => [
            'app'    => [ __DIR__ . '/../templates/app'],
        ],
    ], 
];

Note:
As it will redundant with “app” key registered in config/autoload/templates.global.php, you need to remove “app” key under “paths” in config/autoload/templates.global.php. Don’t forget to remove the “app” directory inside rootproject/templates to avoid confusion.

b. for Album config

// src/Album/config/expressive-config.php
return [
    // ...
    'templates' => [
        'paths' => [
            'album'    => [ __DIR__ . '/../templates/album'],
        ],
    ],
];

Now, you can have the following structure:
expressive-modular-structure-strategy-template

6) Register the “modules” in config/config.php:

use Zend\Expressive\ConfigManager\ConfigManager;
use Zend\Expressive\ConfigManager\PhpFileProvider;

$configManager = new ConfigManager([
    App\AppConfig::class,
    Album\AlbumConfig::class,
    // ...
    
    new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
]);

return new ArrayObject($configManager->getMergedConfig());

It’s done! You can continue with writing middlewares! Tired to follow the steps? Just clone and install from here: https://github.com/samsonasik/learn-expressive-modular

Using Expressive’s middleware in Penny Framework

Posted in Penny Framework, Tutorial PHP, Zend Framework by samsonasik on December 21, 2015

If we are using Penny Framework, we can use Expressive‘s middleware. We can do that with passing callable $next that handle request and response with Penny Event. The $next is simply like the following:

$next = function($request, $response) use ($event) {
   $event->setRequest($request);
   $event->setResponse($response);
};

and we are ready to go! Let’s try to do real example. We need to use los/basepath that can apply “base path” in our application that point to classic-app/public and we can to call: /classic-app/public after our host.

$ composer require los/basepath

We need to invoke the middleware to make it called. So, if we do in ‘bootstrap’ event, we can do:

$eventManager->attach('bootstrap', function($event) {
    $next = function($request, $response) use ($event) {
        $event->setRequest($request);
        $event->setResponse($response);
    };

    $basepathMiddleware = new \LosMiddleware\BasePath\BasePath(
        '/classic-app/public'
    );
    $basepathMiddleware(
        $event->getRequest(),
        $event->getResponse(), 
        $next
    );
});

This is the case if we are using penny skeleton app and we install under ‘classic-app’ directory, and the “front controller” is in public/index.php.

For routed middleware, we can just do the register the middleware as controller:

// config/di.php
// ...
    'router' => function () {
        return \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
            $r->addRoute(
                'GET',
                '/',
                ['App\Controller\IndexController', 'index']
            );

            $r->addRoute(
                'GET',
                '/api/ping',
                ['App\Controller\PingAction', '__invoke']
            );
        });
    },
// ...

That’s it 😉

Middleware implementation in Penny Framework

Posted in Penny Framework, Tutorial PHP by samsonasik on December 6, 2015

Penny is a microframework that has different idea for accomplishing middleware implementation. If we found other frameworks that implements this signatures:

function ($request, $response, $next = null);

In Penny Framework, Middleware implemented via listener that has priority that we call before or/and after. If we need to initialize application flow, you may use ‘bootstrap’ event:

// config/di.php
use App\Middleware\BootstrapMiddleware;
use function DI\decorate;

return [
    'event_manager' => decorate(function($eventManager, $container) {

        $eventManager->attach(
            'bootstrap',
            [$container->get(BootstrapMiddleware::class), 'bootstrap']
        );
        // other attach here...

        return $eventManager;
    }),

    // other service definitions here...
];

We want the middleware executed in all application flow in all controller in the end? use ‘*’ and negative priority:

// config/di.php
use App\Middleware\AllEnderMiddleware;
use function DI\decorate;

return [
    'event_manager' => decorate(function($eventManager, $container) {

        $eventManager->attach(
            '*',
            [$container->get(AllEnderMiddleware::class), 'end'],
            -1
        );
        // other attach here...

        return $eventManager;
    }),

    // other service definitions here...
];

Wanna handle for specific controller’s action ? Use following usage:

// config/di.php
use App\Controller\IndexController;
use App\Middleware\IndexMiddleware;
use function DI\decorate;

return [

    'event_manager' => decorate(function($eventManager, $container) {

            // before ...
            $eventManager->attach(
                IndexController::class.'.index',
                [$container->get(IndexMiddleware::class), 'pre'],
                1
            );

            // after ...
            $eventManager->attach(
                IndexController::class.'.index',
                [$container->get(IndexMiddleware::class), 'post'],
                -1
            );
           // other attach here...

           return $eventManager;
    }),

    // other service definitions here...
];

If we use default Penny EventManager implementation, which is usage Zend Framework 2 EventManager, higher priority will be served first, lower priority will be served later.

If you are familiar with CakePHP, there is already a CakePHP Event implementation, that will serve lower priority first, higher later.

For callable listener, we may create listener service like the following:

namespace App\Middleware;

class IndexMiddleware
{
    public function pre($event)
    {
        $request = $event->getRequest();
        $response = $event->getResponse();

        // manipulate request or/and response here
        $request = $request->withAttribute('foo', 'fooValue');

        // apply manipulated request or/and response
        $event->setRequest($request);
        $event->setResponse($response);
    }

    public function post($event)
    {
        $request = $event->getRequest();
        $response = $event->getResponse();

        // manipulate request or/and response here
        $response = $response->withHeader('X-Powered-By', 'JAVA');

        // apply manipulated request or/and response
        $event->setRequest($request);
        $event->setResponse($response);
    }
}

We can then register the middleware in our Service Container:

//config/di.php
use App\Middleware\AllEnderMiddleware;
use App\Middleware\BootstrapMiddleware;
use App\Middleware\IndexMiddleware;
use function DI\object;

return [
    // ...
    AllEnderMiddleware::class => object(AllEnderMiddleware::class),
    BootstrapMiddleware::class => object(BootstrapMiddleware::class),
    IndexMiddleware::class => object(IndexMiddleware::class),
];