Layout problems with nested modules

In my project i have a admin module which again has a forum submodule. The admin module uses custom layout files and also has a colum1 and column2 layout file. Here’s the file structure:




modules/admin/AdminModule.php

modules/admin/modules/forum/ForumModule.php

modules/admin/modules/forum/controllers/DefaultController.php

modules/admin/views/layouts/main.php

modules/admin/views/layouts/column1.php

modules/admin/views/layouts/column2.php



I have not configured $layout in ForumModule so it uses the parent modules layout. Everything works fine if in DefaultController i use layout main. But it does not work if i want to use column1 or column2. It never renderse the decorating main.php, only column1.php. Here’s the simplified code of column1.php:




<?php $this->beginContent('/layouts/main'); ?>

    <?php echo $content; ?>

<?php $this->endContent(); ?>



The problem is, that when resolving the view file ‘/layouts/main’ for the decorated content, it will always use the layout folder of the current module. This is the forum submodule so it will search in modules/admin/modules/forum/views/layout. It never finds modules/admin/views/layouts/main.php. I even tried to set $layoutPath in ForumModule with no success.

I could only make it work with a pretty dirty workaround like this in column1.php:




<?php $this->beginContent('//../modules/admin/views/layouts/main'); ?>


    <?php echo $content; ?>

<?php $this->endContent(); ?>



Not sure, if we should consider this a bug. Or am i missing something obvious?

I burned several hours on this before tracking the issue to $moduleViewPath in CController->getViewFile(). Initially I assumed I had configured the submodule incorrectly since I was using column1 as my default layout, which results in no layout at all for this scenario.

I’m not sure if it’s plausible, or makes sense in more general terms, but it would be nice if CController->resolveViewFile() could somehow unwind the module hierarchy searching for the view.

Thanks for the work around Mike!

The following resolves the view files bottom up (starting from the most nested module). It is not very efficient, but it solves the problem reusing as much of the existing code as possible.

Add it to your custom Controller. Remove it if the Yii framework itself provides an efficient solution.




class CustomController extends CController {


    public function resolveViewFile($viewName, $viewPath, $basePath, 

            $moduleViewPath = null) {

        

        // First try to find the view file from this module

        $file = parent::resolveViewFile($viewName, $viewPath, $basePath, 

                $moduleViewPath);

        

        if ($file !== false) {

            // We have found the view file.

            // Set the _currentResolveViewFileModule to null in order for 

            // subsequent calls to this method to start recursion.

            $this->_currentResolveViewFileModule = null;

            return $file;

        }

        

        // We have not found a view file. We now recurse into the parent 

        // modules if possible.

        

        // Try to get the parent module.

        if (!isset($this->_currentResolveViewFileModule)) {

            $parentModule = $this->getModule()->getParentModule();

        } else {

            $parentModule = $this->_currentResolveViewFileModule->getParentModule();

        }

        

        if (!isset($this->_currentResolveViewFileModule) && 

                !isset($parentModule)) {

            // If neither the current module nor the parent module is set,

            // we are at the top of the module chain and still have not found

            // the file. We just return false.

            $file = false;

        } else {

            if (isset($parentModule)) {

                // The parent module is set. We will try to find the

                // file in the parent module using the

                // view path of the parent module.

                $moduleViewPath = $parentModule->getViewPath();

                $this->_currentResolveViewFileModule = $parentModule;

            } else {

                // The parent module is not set. We will try to find the

                // view in the basePath.

                $moduleViewPath = null;

            }

            $file = $this->resolveViewFile($viewName, $viewPath, $basePath, 

                    $moduleViewPath);

        }

        return $file;

    }

    

    private $_currentResolveViewFileModule = null;

	

}