Skip to content

Laminas View Integration

Sakya edited this page Jul 15, 2022 · 10 revisions

!!! Laminas PHPRenderer and Laminas View is use by default , this step is no longer necessary !!!

To integrate with Laminas View and PhpRenderer, add below configuration and class to your project.
Remember to change <ProjectNamespace> to your root namespace.

By using Laminas View you can fully use Laminas Form in your project. All form helper will be available except for csrf.

Some view helper is not usable because it coupled tightly with non PSR standard library. Test and check before using it.

Example:
CsrfHelper is coupled tightly with Laminas Session which also coupled tightly with their Laminas Dependency Container.
Laminas Dependency Container not compatible with PSR container, so it is not supported.

Install composer dependency.

composer require laminas/laminas-view

# laminas-form require php-intl extension installed. 
# To skip composer from checking the extension use --ignore-platform-reqs
composer require laminas/laminas-form
composer require laminas/laminas-escaper
composer require laminas/laminas-i18n

Change view configuration file in your project.

<?php

namespace <ProjectNamespace>;

return [
    "view" => [
        "class" => View\LaminasView::class,
        "renderer" => View\Renderer\LaminasPhpRenderer::class
        ...
    ]
];

Add LaminasView Service Factory in service configuration file.

<?php

namespace <ProjectNamespace>;

return [
    "service" => [
        "factories" => [
            View\Renderer\LaminasPhpRenderer::class => View\Renderer\Factory\LaminasPhpRendererFactory::class,
            ...
        ]
    ]
];

Create LaminasView.php class in your project.

<?php
namespace <ProjectNamespace>\View;

use Laminas\View\View as LaminasViewView;
use Laminas\View\ViewEvent as LaminasViewEvent;
use Laminas\View\Model\ViewModel as LaminasViewModel;
use Itseasy\View\ViewInterface;
use Psr\Http\Message\ResponseInterface as Response;

class LaminasView implements ViewInterface
{
    protected $view;
    protected $renderer;
    protected $scripts = [];

    public function __construct()
    {
        $this->view = new LaminasViewView();
    }

    // Call registered ViewHelper in the renderer
    public function __call($function, $args)
    {
        $helper = $this->renderer->getHelperPluginManager();
        $function = $helper->get($function);
        return call_user_func_array($function, $args);
    }

    public function setRenderer($renderer)
    {
        $this->renderer = $renderer;
        $this->view->getEventManager()->attach(
            LaminasViewEvent::EVENT_RENDERER,
            static function () use ($renderer) {
                return $renderer;
            }
        );
    }

    public function appendScript($type, $path, array $options = []):void
    {
        if (in_array($type, ["js", "css"])) {
            $path = ltrim($path, "/");

            $script = new \StdClass();
            $script->type = $type;
            $script->path = sprintf("/%s", $path);
            $script->options = $options;
            $this->scripts[] = $script;
        }
    }

    public function appendScripts(array $scripts = []):void
    {
        foreach ($scripts as $script) {
            if (count($script) == 2) {
                list($type, $path) = $script;
                $options = [];
            } elseif (count($script) == 3) {
                list($type, $path, $options) = $script;
            } else {
                continue;
            }
            $this->appendScript($type, $path, $options);
        }
    }

    public function setLayout(string $layout)
    {
        $this->layout = $layout;
    }

    public function render(Response $response, string $template, array $variables = [], string $layout = "")
    {
        $layoutvars = (empty($variables["layout"]) ? [] : $variables["layout"]);
        $layoutvars = array_merge_recursive($layoutvars, ["scripts" => $this->scripts]);
        $variables = array_diff_key($variables, ["layout" => ["header" => []]]);

        $viewModel = new LaminasViewModel();
        $viewModel->setTemplate($template);
        $viewModel->setVariables($variables);

        $layout = $layout ? : $this->layout;
        $layoutModel = new LaminasViewModel();
        $layoutModel->setTemplate($layout);
        $layoutModel->setVariables($layoutvars);

        $layoutModel->setOption('has_parent', true);
        $layoutModel->addChild($viewModel);

        $response->getBody()->write($this->view->render($layoutModel));
        return $response;
    }

    public function renderJson(Response $response, array $variables = [], ?int $id = null) : Response {
        $jsonModel = new LaminasViewModel();
        $jsonModel->setTemplate("layout/json");
        $jsonModel->setOption('has_parent', true);

        try {
            $variables = [
                "content" => json_encode([
                    "jsonrpc" => "2.0",
                    "id" => (is_null($id) ? time() : $id),
                    "result" => $variables
                ])
            ];
        } catch (Exception $e) {
            $variables = [
                "content" => json_encode([
                    "jsonrpc" => "2.0",
                    "id" => (is_null($id) ? time() : $id),
                    "error" => [
                        "code" => -32603,
                        "message" => $e->getMessage()
                    ]
                ])
            ];
        }
        
        $jsonModel->setVariables($variables);
        $response->getBody()->write($this->view->render($jsonModel));
        return $response->withHeader("Content-type", "application/json");
    }
}
?>

Create LaminasPhpRendererFactory.php class in your project.

<?php

namespace <ProjectNamespace>\View\Renderer\Factory;

use DI;
use Di\Definition\Helper\DefinitionHelper;
use Laminas\Form\ConfigProvider as LaminasFormConfigProvider;
use Laminas\View\Helper\AbstractHelper;
use Laminas\View\HelperPluginManager;
use Laminas\View\Renderer\PhpRenderer;
use Laminas\View\Resolver\TemplatePathStack;
use Psr\Container\ContainerInterface;
use Laminas\Stdlib\ArrayUtils;
use Itseasy\Translator;


class LaminasPhpRendererFactory {
    public function __invoke(ContainerInterface $container) {
        $config = $container->get("Config")->getConfig();
        $viewConfig = $config["view"];
        $viewHelperConfig = $config["view_helpers"];

        // alias for first letter capital to overwrite laminas default helper
        foreach ($viewHelperConfig["aliases"] as $alias => $helper) {
            $viewHelperConfig["aliases"][ucfirst($alias)] = $helper;
        }

        $templateResolver = new TemplatePathStack([
            'script_paths' => [$viewConfig["template_path"]],
            'default_suffix' => $viewConfig["default_template_suffix"]
        ]);

        // Create the renderer
        $renderer = new PhpRenderer();
        $renderer->setResolver($templateResolver);

        $helper_config = [];

        // Include other helper
        $helperConfigs = [
            new LaminasFormConfigProvider(),
        ];

        foreach($helperConfigs as $helperConfig) {
            $helper_config = ArrayUtils::merge($helper_config, $helperConfig->getViewHelperConfig());
        }

        // Merge laminas helper with our own helper
        $helper_config = ArrayUtils::merge($helper_config, $viewHelperConfig);
        $pluginManager = new HelperPluginManager($container, $helper_config);

        $renderer->setHelperPluginManager($pluginManager);

        return $renderer;
    }
}
?>