We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

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?



3.0k
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 :)



1.3k
edited Sep '16

Hello all.

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

client_project src allpication Views vendor base_project 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.

edited Jun '17

I like to organize my views by controller name and function name.

views/index/index_view.php
view/index/example_view.php
views/shared/header_view.php
views/shared/footer_view.php

How can I do this with Phalcon? Do I have to reqister each view subfolder in the loader?



1.3k

Hello mobilize.

You must add all yours paths in the class. Before render the class will scan all added paths and will use the first where have needed template.

Success.