I am trying to create a wrapper for the $this->content of a specific Module.
What I have is a main layout (All the modules will follow this layout) which builds the base layout with headers and footers, etc. These will stay the same across all the Modules.
However I want to have modules with a custom layout for their body content. I.e. for custom nav bars, etc.
So with the following structure:
module
/ ModuleName
/ view
/ layout
/ modulelayout.phtml
/ modulename
/ index
/ index.phtml
view
/ layout
/ layout.phtml
The /view/layout/layout.phtml:
<?= $this->doctype(); ?>
<html lang="en">
<head>
...
</head>
<body>
<header>...</header>
<div id='body'>
<?= $this->content; ?>
</div>
<footer>...</footer>
</body>
</html>
The /module/ModuleName/view/layout/modulelayout.phtml:
<div>...</div>
<div>
<?= $this->content; ?>
</div>
The /module/ModuleName/view/modulename/index/index.phtml:
Hello World
...
So I want all of the actions inside of ModuleName (their $this->content that is displayed), to be wrapped with the layout from modulelayout.phtml.
I created a listener on the dispatch event of a controller to capture it for all controller actions:
public function onBootstrap($e) {
$app = $e->getApplication();
$app->getEventManager()
->getSharedManager()
->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', array($this, 'dispatchControllerStrategy'));
}
Now I need to know how to I retain the base layout, and add my module layout as a wrapper?
public function dispatchControllerStrategy($e) {
$controller = $e->getTarget();
$layout = $controller->layout();
$wrapper = new ViewModel();
$wrapper->setTemplate('layout/modulelayout');
$layout->addChild($wrapper, 'content');
}
^^^ The adding of the child layout does not seem to wrap $this->content in any way, and the child layout does not render. To bring it all together, this is what I expect the final source to look like:
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<header>...</header>
<div id='body'>
<div>...</div>
<div>
Hello World
...
</div>
</div>
<footer>...</footer>
</body>
</html>
Thanks!
Well after a lot of messing around I finally found my solution. And it appears that comment of SlmThreeStepView user2257808 was what I wanted to begin with, but my solution fit my need exactly so I am just going to share it here:
I stopped worrying about trying to modify the layout on controller dispatch and focused on the View with the EVENT_RENDERER_POST event:
->attach('Zend\View\View', \Zend\View\ViewEvent::EVENT_RENDERER_POST, array($this, 'renderViewStrategy'));
I then modify my model on render:
private $renderdedOuterView = false;
public function renderViewStrategy($e) {
if ($this->renderdedOuterView) {
return;
} else {
$this->renderdedOuterView = true;
}
$layout = $e->getModel();
$children = $layout->getChildren();
$layout->clearChildren();
$wrapper = new ViewModel();
$wrapper->setTemplate('layout/modulelayout');
$wrapper->addChild($children[0], 'content');
$layout->addChild($wrapper, 'content');
}
I used $renderedOuterView to only do any render modification for the main layout.
Then for the main layout, I get the Model, and grab the attached child which would be the layout for the current action.
I then clear the main template of that child and add my wrapper in its place. Then as a child of my wrapper, I add the layout for the current action I had just removed from the main template.
My not be the best option, but this solution fits exactly what I was trying to accomplish with my question.
UPDATE
I wanted to add that I found out another issue.
All the onBootstraps for each Module get called, regardless if they are the active module.
This was causing a different module to get this layout. The change was adding an EVENT_DISPATCH first:
$evtMgr = $e->getApplication()->getEventManager();
$evtMgr->attach(MvcEvent::EVENT_DISPATCH, array($this, 'handleEventStrategy'));
Then inside of the handleEventStrategy, I check to make sure that the active Module is the module's bootstrap that is being called. If it is, then I attach the EVENT_RENDER_POST and use the renderViewStrategy that I had defined.
If the active module is not the module with the render view strategy, the if condition will fail and no other module will get that modified layout.
Did you try this module: https://github.com/EvanDotPro/EdpModuleLayouts
And then add the config in your module config file
array(
'module_layouts' => array(
'ModuleName' => 'layout/some-layout',
),
);
Related
I made an view called (head.blade.php) and tried to load it in HomeController __construct function with View::make() function. However, the function works, but not with the variables.
For example, here's function, with View::make():
public function __construct() {
$this->asset = new Asset;
$assets = array('core');
$css = $this->asset->generate($assets);
return View::make('includes.head')->with('styles', $css);
}
If I try to use $styles variable in view, it gives me error: (Undefined variable $styles in...-)
But, digging in Laravel docs I have found this method:
public function __construct() {
$this->asset = new Asset;
$assets = array('core');
$css = $this->asset->generate($assets);
View::creator('includes.head', function($view) use ($css) {
$view->with('styles', $css);
});
}
And the method View::creator works.
My question is, how and why the View::make() doesn't work in __construct?
PS. I'm calling the view in another view with #include method.
In general OOP, you do not return any value from a constructor. The implicit return value of a constructor is the object. Remember that the constructor is called whenever a new object is made:
$myObject = new MyObject(); // <-- I just called the MyObject constructor
Instantiation of a Laravel controller happens prior to the dispatching of the route, so returning the View inside the constructor is logically incorrect. See also this answer.
I'm not sure why you're trying to do this, but I believe it could be because you're trying to attach a 'header' view to all of the views returned by this specific controller. If so, this isn't the way you do this in Laravel. To accomplish that, make a layout view that your other views will extend:
<!-- app/views/layout/master.blade.php -->
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<article>
#include('layout.header')
#yield('content')
#include('layout.footer')
</article>
</body>
</html>
<!-- app/views/layout/header.blade.php -->
<header>
A Header
</header>
<!-- app/views/layout/footer.blade.php -->
<footer>
A Footer
</footer>
<!-- app/views/some-view.blade.php -->
#extends('layout.master')
#section('content')
View Content
#stop
With this setup, some-view.blade.php will have both a header and a footer sandwiching the view's main content.
I'm building an extension that creates a backend module that enables be_users to resize images.
I'm trying to add / include css and javascript files by using the pageRenderer but the files are never included I can only apply css if add it directly in the fluid Template using a style tag and include the javascript file with a script tag.
I tried something like this in the controller
protected $pageRenderer;
....
$this->pageRenderer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Page\\PageRenderer');
$this->pageRenderer->addCssFile('/typo3conf/ext/extKey/Resources/Public/css/styles.css');
$this->pageRenderer->loadJquery();
also tried with a viewHelper
namespace Vendor\ExtKey\ViewHelpers;
class AddJsFileViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper {
public function render() {
$doc = $this->getDocInstance();
$pageRenderer = $doc->getPageRenderer();
$pageRenderer->loadJquery();
}
}
and in my tempate
{namespace pager=Vendor\ExtKey\ViewHelpers}
<f:layout name="Default" />
<f:section name="main">
<pager:addJsFile />
...
still nothing
I'm not sure how you define the template for your backend, but it seems this usually happens using the backend container view helper which already has functions for that:
<f:be.container
addCssFile="{f:uri.resource(path:'css/style.css')}"
addJsFile="{f:uri.resource(path:'js/scripts.js')}">
[your templates content]
</f:be.container>
In TYPO3 7.6.X, It has to be like following
<f:be.container
includeCssFiles="{style:'{f:uri.resource(path:\'css/style.css\')}'}"
includeJsFiles="{script:'{f:uri.resource(path:\'js/script.js\')}'}"
>
<!-- Template Code -->
</f:be.container>
As includeCssFiles and includeJsFiles requires array to be passed, we
can include any number of js and css.
i think the problem was my ViewHelper need to renderChilden and start/end page
current implementation is like this
the ViewHelper
namespace Vendor\ExtKey\ViewHelpers;
class AddPublicResourcesViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper {
public function render() {
$doc = $this->getDocInstance();
$pageRenderer = $doc->getPageRenderer();
$extRelPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath("ext_key");
$pageRenderer->addCssFile($extRelPath . "Resources/Public/css/styles.css");
$pageRenderer->loadJquery();
$pageRenderer->addJsFile($extRelPath . "Resources/Public/js/app.js");
$output = $this->renderChildren();
$output = $doc->startPage("title") . $output;
$output .= $doc->endPage();
return $output;
}
}
the template
{namespace pager=Vendor\ExtKey\ViewHelpers}
<f:layout name="Default" />
<f:section name="main">
<pager:addPublicResources />
Pagerender::loadJjquery is working and accessible like this
TYPO3.jQuery(function($) {
});
I need to compose a web page of several view templates (the view template rendering page content and a view template rendering sidebar). In my layout.phtml, I have two variable placeholders: $content and $sidebar:
......
<?php echo $this->sidebar; ?>
......
<?php echo $this->content; ?>
......
In my controller's action, I pass the data to these view templates through the ViewModels chained in a tree:
public function indexAction() {
// Preparing my data
// $form = ...
// $menuItems =
// $activeItem =
// Create sidebar view model
$sidebarViewModel = new ViewModel(array('menuItems'=>$menuItems, 'activeItem'=>$activeItem));
// Add it as a child to layout view model
$this->layout()->addChild($sidebarViewModel, 'sidebar');
// Page content view model
$viewModel = new ViewModel(array('form'=>$form));
return $viewModel;
}
But, because I have the sidebar on every page, I will have to copy and paste this code for every action of every controller. Is there any recommended way of reusing the code that populates the ViewModel for sidebar?
One approach would be to achieve this with a controller plugin.
Assuming you have wired it up with appropriate config, and you're in the Application module.
In module/Application/src/Application/Controller/Plugin/AddSidebar.php:
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class addSidebar extends AbstractPlugin {
public function __invoke($menu, $active) {
// create new view model
$sidebarVM = new ViewModel(array(
'menuItems' => $menu,
'activeItem' => $active
));
// add it to the layout
$this->getController()->layout()->addChild($sidebarVM, 'sidebar');
}
}
Then in each of your controllers:
$this->addSidebar($menuItems, $activeItem);
Another (probably better) option would be to hook into the render MvcEvent and add the sidebar there. You'd have to work out how to generate $menuItems and $activeItem in that context however.
I am working on my own MVC framework. Below is an example controller I have so far.
I have a way of loading models into my controller and also view files.
I am wanting to also have different template options for my site. My template will just be a page layout that inserts the views that are created from my controller into the middle of my template file.
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
// load a Model
$this->loadModel('profile');
//GET data from a Model
$profileData = $this->profile_model->getProfile($userId);
// load view file and pass the Model data into it
$this->view->load('userProfile', $profileData);
}
}
Here is a basic idea of the template file...
DefaultLayout.php
<!doctype html>
<html lang="en">
<head>
</head>
<body>
Is the controller has data set for the sidebar variable, then we will load the sidebar and the content
<?php if( ! empty($sidebar)) { ?>
<?php print $content; ?>
<?php print $sidebar; ?>
If no sidebar is set, then we will just load the content
<?php } else { ?>
<?php print $content; ?>
<?php } ?>
</body>
</html>
Another Template without any header, footer, anything else, can be used for AJAX calls
EmptyLayout.php
<?php
$content
?>
I am looking for ideas on how I can load my main template file and then include and view files into the content area of my main layout file?
In the sample layout file, you can see that the content area has a variable called $content. I am not sure how I can populate that with the views content, to be inserted into my main layout template. If you have any ideas, please post sample
Something a little bit like
function loadView ($strViewPath, $arrayOfData)
{
// This makes $arrayOfData['content'] turn into $content
extract($arrayOfData);
// Require the file
ob_start();
require($strViewPath);
// Return the string
$strView = ob_get_contents();
ob_end_clean();
return $strView;
}
Then use with
$sidebarView = loadView('sidebar.php', array('stuff' => 'for', 'sidebar' => 'only');
$mainView = loadView('main.php', array('content' => 'hello',, 'sidebar' => $sidebarView);
How do people construct websites with cake/CI ect... for easy maintenance on the html?
I can put each of the sections in its own view file and make the website that way:
<div id="header"></div> <!-- header_view.php -->
<div id="content"> <!-- header_view.php -->
<div id="left_column"></div> <!-- page_x_view.php -->
<div id="center_column"></div> <!-- page_x_view.php -->
</div>
<div id="footer"></div> <!-- footer_view.php -->
But each page_x_view.php file would contain
<div id="left_column"><!-- Content --></div>
<div id="center_column"><!-- Content --></div>
And I'm duplicating these items through each of the files, so if I need to change the column structure then it is not easy.
Hopefully I am clear.
I have a controller caled MY_Controller which has a method that renders the complete page. I extend all my controllers from this main controller. HOw this helps? My main controllers takes a view and embedds it in the main content area of page and assembles a complete page. This controller takes header, footer, sidebar views and does all the mambo jumbo. Its very easy to develop such a system in CI. Some this call two step or multiple views. So if some random day I have to change layout of my page I just need to look at MY_Controller.
Cake on the other side uses layouts. I have done just one project in CakePHP so am not that expert but you can achieve the same effect in any framework. Here is how I do it in CI
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Controller extends CI_Controller
{
public function __construct()
{
parent::__construct();
log_message('debug', 'Controller Library '.__CLASS__ . ' ('. __FILE__ .') loaded.');
$this->properties['viewPath'] = $this->config->item('viewPath');
$this->setPageMetaData();
$this->setFavIcon();
}
public function render($viewData = null, $data=null)
{
$data = array(
'headerLayout' => $this->printHeaderLayout(array_merge($this->properties, (isset($data['headerLayout'])?$data['headerLayout']:array()))),
'leftLayout' => $this->printLeftLayout(array_merge($this->properties, (isset($data['leftLayout'])?$data['leftLayout']:array()))),
'rightLayout' => $this->printRightLayout(array_merge($this->properties, (isset($data['rightLayout'])?$data['rightLayout']:array()))),
'footerLayout' => $this->printFooterLayout(array_merge($this->properties, (isset($data['footerLayout'])?$data['footerLayout']:array()))),
'containerLayout' => $viewData,
);
this->load->view($this->properties['viewPath'].'layout/layout.php', $data);
}
public function setPageMetaData($pageMetaData=null)
{
$this->properties['pageTitle'] = isset($pageMetaData['pageTitle'])? $pageMetaData['pageTitle'] : $this->config->item('pageTitle');
$this->properties['pageKeywords'] = isset($pageMetaData['pageKeywords'])? $pageMetaData['pageKeywords'] : $this->config->item('pageKeywords');
$this->properties['pageDescription'] = isset($pageMetaData['pageDescription'])? $pageMetaData['pageDescription'] : $this->config->item('pageDescription');
}
public function setFavIcon($favIcon=null)
{
$this->properties['favIcon'] = (null !== $favIcon) ? $favIcon : $this->config->item('favIcon');
}
public function printHeaderLayout($data=null)
{
return ($this->load->view($this->properties['viewPath'].'layout/header', $data, true));
}
public function printFooterLayout($data=null)
{
return( $this->load->view($this->properties['viewPath'].'layout/footer', $data, true));
}
public function printLeftLayout($data=null)
{
return($this->load->view($this->properties['viewPath'].'layout/left', $data, true));
}
public function printRightLayout($data=null)
{
return($this->load->view($this->properties['viewPath'].'layout/right', $data, true));
}
}
Do note that this is not the exact code. I had to modify it for you, so do not blindly user it. If you know CI you will understand that I have setup paths to view in a config file. This helps me in setting up two totally different themes and use same controller. I can also add authentication layer which will based on user authentication/cookies can show a login or logout link in header. This is a template which I keep change and I extend all my controllers from MY_Controller and use in my controllers I simply do
$viewDataForForm = $this->load->view($this->properties['viewPath'].'homepage/some-form', array(), true);
$viewDataForContent = $this->load->view($this->properties['viewPath'].'homepage/some-content', array(), true);
$this->render($viewDataForForm.$viewDataForContent);
HTH!
Codeigniter and CakePHP take advantage of the Model View Controller configuration. They separate the database queries and data processing from the views. This provides an easy to use and easy to maintain way of coding. Multiply controllers can use the same view which helps cut down on the amount of code written and the complexity. Methods in the models can be reused which reduces bugs and amount of code written. And controllers provide and easy to follow way of reading and writing code. I am not sure if I answered your question but comment on my answer if you need and more explanation.