PHP Classes

File: src/App/View/ViewRenderer.php

Recommend this page to a friend!
  Packages of Thierry Feuzeu   Jaxon   src/App/View/ViewRenderer.php   Download  
File: src/App/View/ViewRenderer.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Jaxon
Call PHP classes from JavaScript using AJAX
Author: By
Last change:
Date: 1 year ago
Size: 7,946 bytes
 

Contents

Class file image Download
<?php

namespace Jaxon\App\View;

use Jaxon\Config\Config;
use Jaxon\Di\Container;

use Closure;

use function array_filter;
use function array_merge;
use function is_array;
use function rtrim;
use function strrpos;
use function substr;

class ViewRenderer
{
    /**
     * @var Container
     */
    protected $di;

    /**
     * The view data store
     *
     * @var Store
     */
    protected $xStore = null;

    /**
     * The view data store
     *
     * @var Store
     */
    protected $xEmptyStore = null;

    /**
     * The view namespaces
     *
     * @var array
     */
    protected $aNamespaces = [];

    /**
     * The default namespace
     *
     * @var string
     */
    protected $sDefaultNamespace = 'jaxon';

    /**
     * The view global data
     *
     * @var array
     */
    protected $aViewData = [];

    /**
     * The class constructor
     *
     * @param Container $di
     */
    public function __construct(Container $di)
    {
        $this->di = $di;
        $this->xEmptyStore = new Store();
    }

    /**
     * Add a view namespace, and set the corresponding renderer.
     *
     * @param string $sNamespace    The namespace name
     * @param string $sDirectory    The namespace directory
     * @param string $sExtension    The extension to append to template names
     * @param string $sRenderer    The corresponding renderer name
     *
     * @return void
     */
    public function addNamespace(string $sNamespace, string $sDirectory,
        string $sExtension, string $sRenderer): void
    {
        $aNamespace = [
            'directory' => $sDirectory,
            'extension' => $sExtension,
            'renderer' => $sRenderer,
        ];
        $this->aNamespaces[$sNamespace] = $aNamespace;
    }

    /**
     * Set the view namespaces.
     *
     * @param Config $xAppConfig    The config options provided in the library
     *
     * @return void
     */
    public function addNamespaces(Config $xAppConfig): void
    {
        if(empty($aNamespaces = $xAppConfig->getOptionNames('views')))
        {
            return;
        }

        $sPackage = $xAppConfig->getOption('package', '');
        foreach($aNamespaces as $sNamespace => $sOption)
        {
            // Save the namespace
            $aNamespace = $xAppConfig->getOption($sOption);
            $aNamespace['package'] = $sPackage;
            if(!isset($aNamespace['renderer']))
            {
                $aNamespace['renderer'] = 'jaxon'; // 'jaxon' is the default renderer.
            }
            $this->aNamespaces[$sNamespace] = $aNamespace;
        }
    }

    /**
     * Get the view renderer
     *
     * @param string $sId    The unique identifier of the view renderer
     *
     * @return ViewInterface
     */
    public function getRenderer(string $sId): ViewInterface
    {
        // Return the view renderer with the given id
        return $this->di->g("jaxon.app.view.$sId");
    }

    /**
     * Add a view renderer with an id
     *
     * @param string $sId    The unique identifier of the view renderer
     * @param Closure $xClosure    A closure to create the view instance
     *
     * @return void
     */
    public function addRenderer(string $sId, Closure $xClosure): void
    {
        // Return the initialized view renderer
        $this->di->set("jaxon.app.view.$sId", function($di) use($sId, $xClosure) {
            // Get the defined renderer
            $xRenderer = $xClosure($di);
            // Init the renderer with the template namespaces
            $aNamespaces = array_filter($this->aNamespaces, function($aOptions) use($sId) {
                return $aOptions['renderer'] === $sId;
            });
            foreach($aNamespaces as $sName => $aOptions)
            {
                $xRenderer->addNamespace($sName, $aOptions['directory'], $aOptions['extension']);
            }
            return $xRenderer;
        });
    }

    /**
     * Add a view renderer with an id
     *
     * @param string $sId    The unique identifier of the view renderer
     * @param string $sExtension    The extension to append to template names
     * @param Closure $xClosure    A closure to create the view instance
     *
     * @return void
     */
    public function setDefaultRenderer(string $sId, string $sExtension, Closure $xClosure): void
    {
        $this->setDefaultNamespace($sId);
        $this->addNamespace($sId, '', $sExtension, $sId);
        $this->addRenderer($sId, $xClosure);
    }

    /**
     * Get the view renderer for a given namespace
     *
     * @param string $sNamespace    The namespace name
     *
     * @return ViewInterface|null
     */
    public function getNamespaceRenderer(string $sNamespace): ?ViewInterface
    {
        if(!isset($this->aNamespaces[$sNamespace]))
        {
            return null;
        }
        // Return the view renderer with the configured id
        return $this->getRenderer($this->aNamespaces[$sNamespace]['renderer']);
    }

    /**
     * Set the default namespace
     *
     * @param string $sDefaultNamespace
     */
    public function setDefaultNamespace(string $sDefaultNamespace): void
    {
        $this->sDefaultNamespace = $sDefaultNamespace;
    }

    /**
     * Get the current store or create a new store
     *
     * @return Store
     */
    protected function store(): Store
    {
        if(!$this->xStore)
        {
            $this->xStore = new Store();
        }
        return $this->xStore;
    }

    /**
     * Make a piece of data available for the rendered view
     *
     * @param string $sName    The data name
     * @param mixed $xValue    The data value
     *
     * @return ViewRenderer
     */
    public function set(string $sName, $xValue): ViewRenderer
    {
        $this->store()->with($sName, $xValue);
        return $this;
    }

    /**
     * Make a piece of data available for all views
     *
     * @param string $sName    The data name
     * @param mixed $xValue    The data value
     *
     * @return ViewRenderer
     */
    public function share(string $sName, $xValue): ViewRenderer
    {
        $this->aViewData[$sName] = $xValue;
        return $this;
    }

    /**
     * Make an array of data available for all views
     *
     * @param array $aValues    The data values
     *
     * @return ViewRenderer
     */
    public function shareValues(array $aValues): ViewRenderer
    {
        foreach($aValues as $sName => $xValue)
        {
            $this->share($sName, $xValue);
        }
        return $this;
    }

    /**
     * Render a view using a store
     *
     * The store returned by this function will later be used with the make() method to render the view.
     *
     * @param string $sViewName    The view name
     * @param array $aViewData    The view data
     *
     * @return Store   A store populated with the view data
     */
    public function render(string $sViewName, array $aViewData = []): Store
    {
        $xStore = $this->store();
        // Get the default view namespace
        $sNamespace = $this->sDefaultNamespace;
        // Get the namespace from the view name
        $nSeparatorPosition = strrpos($sViewName, '::');
        if($nSeparatorPosition !== false)
        {
            $sNamespace = substr($sViewName, 0, $nSeparatorPosition);
            $sViewName = substr($sViewName, $nSeparatorPosition + 2);
        }

        $xRenderer = $this->getNamespaceRenderer($sNamespace);
        if(!$xRenderer)
        {
            // Cannot render a view if there's no renderer corresponding to the namespace.
            return $this->xEmptyStore;
        }

        $xStore->setData(array_merge($this->aViewData, $aViewData))
            ->setView($xRenderer, $sNamespace, $sViewName);

        // Set the store to null so a new store will be created for the next view.
        $this->xStore = null;
        // Return the store
        return $xStore;
    }
}