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.
Related
I have a controller named 'Student' with two actions called 'index' and 'add'.
I want to load different css files for each of the action. So far i have tried is, i have imported Html Helper, created object of it and called its css method. When i run it, it is not throwing any error, nor showing expected result. Means, it is not loading css file dynamically in my view.. How can i dynamically load css files in different views from Controller?
Code:-
<?php
App::import('Helper','Html');
class StudentController extends AppController
{
public function index()
{
// $current_controller = $this->params['controller'];
// echo $current_controller;
//$view=new View(new Controller($current_controller));
//$Html=new HtmlHelper($view);
$Html=new HtmlHelper(new View(null));
//$html=new HtmlHelper(new View());
$Html->css('cake.generics');
//echo ;
//$this->Html->css("cake.generics");
}
public function add()
{
// $current_controller = $this->params['controller'];
// echo $current_controller;
$html=new HtmlHelper(new View(null));
$html->css("mystyle.css");
}
}
You can do it in view file e.g
//in your View/Students/add.ctp
$this->Html->css('yourstyle', array('block' => 'yourStyle'));
//in your layout file
echo $this->fetch('yourStyle');
the same with js files
// in your view
$this->Html->script('yourjs', array('block' => 'yourJs'));
//in your layout file
echo $this->fetch('yourJs');
you can also create a global layout file in View > Element like default_assets.ctp
after that add this file in your default layout file like,default_layout.ctp in View > Layout folder
and after access this in your controller like
public function index(){
$this->layout = "default_layout";
}
I got it working in this way,
I have added "mycss" variable in controller, index action:-
$this->set('mycss','custom');
And accessed this mycss variable from layout file:-
if(isset($mycss)){
$this->Html->css("$mycss");
}
And it worked.
I want to write a plugin in ZF2,
An example of the plugin is a like button that shows in every post. It should for example print in PostsAction,
I know I can use:
$like = $this->forward()->dispatch('Application\Controller\Index', array(
'action' => 'like',
'postId' => $Id
));
$like variable returns a button that users can click on.
But I want to echo this in the view. In forward the view is not defined.
Also if I use
return $this->getView()->render('application/index/like', array('postId' => $Id));
I don't have access to postId in likeController, because it is set in the view. How I can implement these type of plugins that need a dynamic variables?
Looks like you only need partials. A partial in ZF2 is only a view which you print in another view and give some params to it.
So you could define a View:
// application/partials/button.phtml
<button data-postId="<?php echo $this->postId ?>">Like It!</button>
And use it in other View:
echo $this->partial('application/partials/button.phtml', array(
'postId' => $thePostId
));
Official Documentation
Nice Answer on SO to implement with template_map
Solution using view helper
I think what you are looking for is a custom view helper. You can read on this in the official ZF2 documentation.
You have to write your custom button view helper, register it and then you can use it in your view.
The helper class:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LikeButtonHelper extends AbstractHelper
{
public function __invoke($post)
{
//return here your button logic, you will have access to $post
}
}
Register your helper within a configuration file:
'view_helpers' => array(
'invokables' => array(
'likeButtonHelper' => 'Application\View\Helper\LikeButtonHelper',
),
)
And finally in the view you can use it like this:
foreach($posts as $post){
echo( ... your code to show the post ...);
echo $this->likeButtonHelper($post);
}
UPDATE - Solution using forward plugin
I think I get what you mean now. I also think the example you are talking about is what in the ZF2 forward plugin documentation is referred to as “widgetized” content.
I think you are doing it correctly. You can attach the return value $like as a child to the view of the original controller (from where you forwarded in the first place).
So in your WidgetController:
use Zend\View\Model\ViewModel;
class WidgetController extends AbstractActionController
{
public function likeAction()
{
$post= $this->params()->fromRoute('post');
$viewModel = new ViewModel(array('post' => $post));
$viewModel->setTemplate('view/widgets/like');
return $viewModel;
}
}
So in your PostController:
use Zend\View\Model\ViewModel;
class PostController extends AbstractActionController
{
public function postsAction()
{
$likeWidget = $this->forward()->dispatch('Application\Controller\WidgetController', array(
'action' => 'like',
'post' => $post
));
$viewModel = new ViewModel();
$viewModel->setTemplate('view/posts/post');
$viewModel = new ViewModel(array(
//...add your other view variables...
));
// Add the result from the forward plugin as child to the view model
if ($likeWidget instanceof ViewModel)
{
$viewModel->addChild($likeWidget , 'likeWidget');
}
return $view;
}
}
And finally in your post view template add:
echo($this->likeWidget);
That is where the widget will eventually output.
The problem remains that you can not do this inside a foreach loop (a loop for printing your posts) in the view. That is why I suggested using a view helper and #copynpaste suggests using a partial, those are more suitable for adding additional logic inside a view.
Note:
Personally I don't like this forward solution for something so simple as a like button. There is hardly any logic in the controller and it seems overly complicated. This is more suitable for reusing a whole view/page that will be both rendered by itself as well as nested in another view.
The partials or view helpers seem much more suitable for what you want to do and those are very proper ZF2 solutions.
I found it ,developed by Mohammad Rostami,Special thanks to him :
Plugin In ZF2
i have small trouble...
class Controller {
init() {
// initializing...
// render header && footer
$header = (new HeaderAction)->run();
$footer = (new FooterAction)->run();
// redirect to called action, what renders all the content
}
}
What i can detect diff between ->run() and called action?
Answer found in:
AfterRender -> parse Route -> compare action names. If match - echo, if not match - Return.
Yii is SHIT!!!
I will write new class MyAction with method AddData, what can render for me some viewfile. Creating class CAction for this, what can't rendering? Maybe i must create controller? Are you noob, Quang, ha?
Lol, i can't create the header with action. I must create it in Controller. Controller file now is 1200 lines. >_<
class MyAction {
public $data = array();
public function addData($name, $val) {
$this->data[$name] = $val;
}
public function render($file) {
ob_start;
// ... something
return ob_get_clean;
};
}
/// ITS ALL WHAT NEED ALL THE DEVELOPERS>>>>
BEHAVIORS? EVENTS? FILTERS? WIDGETS? MODULES? MAYBE WE NEED "CAMOBAP" AS AUTOCAR?
REASOOOONS????
===
Lol, there is model cannot to render at all. I have products with views as "tr-tr", and i must create controller, create action, create route for rendering funcking 10 SYMBOLS.... Its Rage. About u, Quang.
Russian Bear will kill you.
I am new to Joomla, I want to know how the Joomla controller passes data to the model, model to controller and controller to view. Although this might be a silly question, I really tried to find the answer. I hope I can get some help from the stackoverflow family.
The controller picks up the view variable in the url and using these determines which view needs to be used. It then sets the view to be used. The view then calls the model to fetch the data it requires and then passes this to the tmpl to be displayed.
Below is a simple setup of how this all works together:
components/com_test/controller.php
class TestController extends JController
{
// default view
function display() {
// gets the variable some_var if it was posted or passed view GET.
$var = JRequest::getVar( 'some_var' );
// sets the view to someview.html.php
$view = & $this->getView( 'someview', 'html' );
// sets the template to someview.php
$viewLayout = JRequest::getVar( 'tmpl', 'someviewtmpl' );
// assigns the right model (someview.php) to the view
if ($model = & $this->getModel( 'someview' )) $view->setModel( $model, true );
// tell the view which tmpl to use
$view->setLayout( $viewLayout );
// go off to the view and call the displaySomeView() method, also pass in $var variable
$view->displaySomeView( $var );
}
}
components/com_test/views/someview/view.html.php
class EatViewSomeView extends JView
{
function displaySomeView($var) {
// fetch the model assigned to this view by the controller
$model = $this->getModel();
// use the model to get the data we want to use on the frontend tmpl
$data = $model->getSomeInfo($var);
// assign model results to view tmpl
$this->assignRef( 'data', $data );
// call the parent class constructor in order to display the tmpl
parent::display();
}
}
components/com_test/models/someview.php
class EatModelSomeView extends JModel
{
// fetch the info from the database
function getSomeInfo($var) {
// get the database object
$db = $this->getDBO();
// run this query
$db->setQuery("
SELECT
*
FROM #__some_table
WHERE column=$var
");
// return the results as an array of objects which represent each row in the results set from mysql select
return $db->loadObjectList();
}
}
components/com_test/views/someview/tmpl/someviewtmpl.php
// loop through the results passed to us in the tmpl
foreach($this->data as $data) {
// each step here is a row and we can access the data in this row for each column by
// using $data->[col_name] where [col_name] is the name of the column you have in your db
echo $data->column_name;
}
check out this site for detailed tutorial on how to make components and modules using Joomla's MVC. Hope it helps
https://docs.joomla.org/Developing_a_MVC_Component
Also refer official joomla doc for detailed tutorial on how to make components and modules using Joomla's MVC. Hope it helps
http://docs.joomla.org/Developing_a_Model-View-Controller_Component/1.5/Introduction
I currently have a search form in the search controller, so the only way I can get to it is through /search/. I have to refactor my code so that this search form appears not only in the Search Controller but also globally throughout the site.
( The code isnt exact as I had to retype some of it )
My class that extends Zend_Form is located in application/forms/forms/SearchForm.php:
class Form_SearchForm extends Zend_Form {
public function init() {};
}
My search controller is something like..
class SearchController extends Zend_Controller_Action
{
public function search() {
$searchForm = new Form_SearchForm();
$this->view->form = $searchForm;
}
}
In my Bootstrap.php I have an autoloader for models:
protected function _initAutoload() {
$autoLoader = Zend_Loader_Autoloader::getInstance();
$resourceLoader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'form' => array(
'path' => 'forms',
'namespace' => 'Form_',
),
'model' => array(
'path' => 'models/',
'namespace' => 'Model_',
),
),
)
);
return $autoLoader;
}
I'm wondering where I can store my code so that globally the search form is generated in the view.
My global layout file is located in application/layouts/scripts/layout.phtml and currently spits out a dynamic content area:
<div id="main">
<?php echo $this->layout()->content;?>
</div>
Should I just add the form to this layout.phtml or is there some generic controller I should use?
Edit: Sorry for not specifying this too, but what if for example I wanted to not include it for 1-2 special pages ( maybe an admin section ).. if I hardcoded it into layout.phtml it would still appear.. or should I serve a different layout file to say, an admin area?
Creating a searchAction() is not good for performance because it requires a brand new dispatch cycle. If, and only if, you have very complex logic that justifies a separate action, you could create a Controller Plugin and add searchAction() to the ActionStack. If you are only instantiating/assigning the form or if you don't need the search form for every request, it's not an optimal solution.
Another possibility would be to instantiate and assign the form in the bootstrap. This kind-of breaks separation of concerns, but provides better performance.
protected function _initSearchForm()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$searchForm = new Form_SearchForm();
$view->searchForm = $searchForm;
return $searchForm;
}
Finally, my preferred solution would be a custom view helper:
<?php
class My_View_Helper_SearchForm extends Zend_View_Helper_Abstract
{
public function searchForm()
{
$searchForm = new Form_SearchForm();
return $searchForm;
}
}
For either of these solutions, you'd ideally output the form in your layout file to minimise duplication.
layout.phtml:
<?php echo $this->searchForm() ?>
And create an alternate layout admin.phtml for admin area pages. This gives you the flexibility to change the admin pages significantly when new requirements pop up.
You can create your Form in a Controller Plugin and add it to view vars somehow (by Zend_Controller_Front?), which are accessible in layout, too. But it's too complicated in current ZF version (or I'm too dumb)
You can make Form_SearchForm a singleton
class Form_SearchForm ... {
static function getInstance() {
static $instance;
if (!$instance)
$instance = new Form_SearchForm();
return $instance;
}
}
Now instead of creating new Form_SearchForm() just get it as
$form = Form_SearchForm::getInstance();
You can put an instance of Form_SearchForm to the registry
I probably have missed a very cool a simple way :)
I would split it into a partial and a place holder.
in layout.phtml:
<?php if($searchForm = $this->placeHolder('searchForm'): ?>
<?php echo $searchForm; ?>
<?php endif; ?>
then in your views you can call:
<?php $this->placeHolder('searchForm')->set($this->partial('search-from.phtml', 'search')); ?>
IF you wanted you could even make a search view helper that basically does the place holder call.
The Controller plugin would be better if you have more pages that dont need it than d though. I would still probably use placeholder though to accomplish it. That way you can easily override or append to it later on a view-by-view basis without calling anything on the front controller.