Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Multiple View Directories

Can I have multiple view directories, searching for a script name?

Edit: ... and where do we use the _activeRenderPath?

https://github.com/phalcon/cphalcon/blob/master/ext/mvc/view.c#L997

Edit2: ... and I found an issue.

https://github.com/phalcon/cphalcon/issues/1630

I needed pretty much the same thing. I find the view is pretty restrictive as it forces relative views. I just added a view override in the engineRender: https://github.com/Mech7/Phapp/blob/master/src/Mvc/View.php#L20

edited Jun '14

Overriding _engineRender, Phalcon will search for scripts inside the same directory, right?

I need something like this:

$view->setViewsPath('./module/Admin/view');
$view->addVendorViewsPath('./vendor/Auth/view');

... with this structure:

project
|-- module
|   |-- Admin
|   |   |-- view
|   |   |   |-- index.phtml
+-- vendor
|   |-- Auth
|   |   |-- view
|   |   |   |-- users
|   |   |   |   |-- index.phtml

Why? Same Admin/view/index.phtml for all "Admin" resources. Auth namespace registers controllers

$di->set('Admin\Controller\Users', 'Auth\Controller\Users');

... with view action scripts inside vendor Auth namespace.

Am I crazy? XD

edited Jun '14

Yes this is possible.. in your Module.php do something like:

    public function registerServices($di) {
        //Registering the view component
        if ($di->get('view')) {
            $view = $di->get('view');
        } else {
            $view = new \Phalcon\Mvc\View();
        }
        $view->setViewsDir(realpath(__DIR__.'/..//view/'));
    }

Overriding renderEngine template will look wherever you change it to look for. For me it looks in a template path.

How Phalcon will know my Admin path?



1.6k
Accepted
answer
edited Jun '14

It depends where are your overrides.. my structure is a little different i have something like:

vendor/cept/blog/view
vendor/cept/blog/src/Module.php

So this is my main dir which is set in Module.php by: $view->setViewsDir(realpath(DIR.'/..//view/')); Then my overrides are here:

theme/default/{modulename}/{controllername}/{action}

The override location is set by: $view->setThemePath(getcwd().'/theme/default/');

But you can do the same with an override for your paths :) Still is a shame this is not really possible with some event in the view class

Damn! After one thousand years I understood!

I forgot the "renderLevel"... Let me try! =D

ps: my vim was in case sensitive search --' https://github.com/phalcon/cphalcon/blob/1.3.2/ext/mvc/view.c#L1298

ps2: ... and if we create an event? :)

Analyzing code, we can't use the view:beforeRenderView event to change the path, 'cause it'll be only triggered if file exists. So, we can't use that in this case.

So, if I override the _engineRender, arguments can't be defined to keep the structure, following the method definition. So...

<?php
namespace System\Mvc\View;
use Phalcon\Mvc\View as PhalconView;
class View extends PhalconView {
    protected function _engineRender() {
        $manager = $this->getEventsManager();
        if ($manager) {
            $manager->fire('view:beforeEngineRender', $this);
        }
        $result = call_user_func_array('parent::_engineRender', func_get_args());
        if ($manager) {
            $manager->fire('view:afterEngineRender', $this);
        }
        return $result;
    }
}

... and I registered these callbacks:

$manager = $di->get('eventsManager');
$manager->attach('view:beforeEngineRender', function($event, $component) use ($di){
    if (View::LEVEL_ACTION_VIEW == $component->getCurrentRenderLevel()) {
        $dispatcher = $di->get('dispatcher');
        if ($dispatcher->getModuleName() == 'Admin') {
            switch ($dispatcher->getControllerName()) {
                case 'users':
                    $component->setBasePath('./vendor/Auth');
                    break;
            }
        }
    }
});
$manager->attach('view:afterEngineRender', function($event, $component){
    $component->setBasePath('./module/Admin');
});

... AND IT WORKS LIKE A CHARM!

THANK YOU @Mech7!

Ah the event manager is a nice touch ;)

thank you :) I forgot to check if the view:beforeEngineRender was calcelled, but it works :)



579
edited Sep '16

Hello all.

I have similar problem. I have base project and client project.

clientproject src allpication Views vendor baseproject src application Views

I need to use teplates from both Views folders.

To solve my problem i have following class:

<?php
namespace Your\namespace;

class View extends \Phalcon\Mvc\View {
    protected $pathList = [];
    protected $firstDir = null;

    public function __construct($options = null) {
        parent::__construct($options);
    }

    public function addPath($path) {
        $this->pathList[] = $path;
    }

    public function setPths($paths) {
        $this->pathList = $paths;
    }

    public function getPaths() {
        return $this->pathList;
    }

    protected function _engineRender($engines, $viewPath, $silence, $mustClean, $cache = null) {
        if (null === $this->firstDir) {
            $this->firstDir = $this->_viewsDir;
        }
        $this->_viewsDir = $this->firstDir;

        $extentions = array_keys($engines);
        foreach ($this->pathList as $path) {
            foreach ($extentions as $extention) {
                $dir = $this->_basePath.$path.$viewPath.$extention;
                if (file_exists($dir)) {
                    $this->_viewsDir = $path;
                    break 2;
                }
            }
        }
        parent::_engineRender($engines, $viewPath, $silence, $mustClean, $cache);
    }
}

Maybe this will help someone other too.

P.S. Sorry for bad layout.