I have a Yii application and in the layout I have a static sidebar. I would like to change the content of the sidebar depending on the controller action, or the view. At the moment, in the views/layouts/main.php file, I have a sidebar defined as:
<div class="sidebar">hello!</div>
When the controller is image and the action is test, I would like to display this:
<div class="sidebar">Created at <? echo $image->created_at; ?></div>
The $image object is defined in the image controller and the test action.
In short, I would like to display a sidebar when the controller is image and the action is test, however, I would not like to have a different layout entirely. How can I accomplish this?
This is work for clip. In your layout:
<div class="sidebar">
<?php if(!empty($this->clips['sidebar'])) {
echo $this->clips['sidebar'];
} else { ?>
hello
<?php } ?>
</div>
Somewhere in your view:
<?php $this->beginClip('sidebar'); ?>
Created at <? echo $image->created_at; ?>
<?php $this->endClip(); ?>
You can put a simple if-else check, using getController(), getAction() like this :
if(Yii::app()->getController()->getId()=='image' && Yii::app()->getController()->getAction()->getId()=='test'){
echo '<div class="sidebar">Created at '.$this->image->created_at.'</div>';
}
else
echo '<div class="sidebar">hello!</div>';
To access $image in a layout we need to make it a public property of the image controller itself, then in the action test where we initialize the $image, we will initialize the public $image, example:
class ImageController extends Controller{
public $image;
...
public function actionTest(){
$this->image=Example::model()->findByPk(1);// example
...
}
}
This will allow us to access image in the layout albeit using $this->image->property.
Also in the layout $this refers to the current controller, hence we can also use $this->id instead of Yii::app()->getController()->getId() to get the controller id(name which is image here) and $this->action instead of Yii::app()->getController()->getAction()->getId() to get the action id.
Related
I don't want to add child to the viewModel in action controller:
// action controller
public function indexAction() {
$result = new ViewModel();
$result->setTemplate('application/view/another-action');
$comments = new ViewModel();
$comments->setTemplate('application/view/child-comments');
$result->addChild($comments, 'child_comments');
return $result;
}
...
// View
<div>
<?php echo $this->child_comments ?>
</div>
I want to include view in another view:
<div>
<?php
$view = new ViewModel();
$view->setVariables($this->var);
$view->setTemplate('page_nav.phtml');
// here I want to render view
?>
</div>
Is it possible?
This is what the Partial view helper does (docs seem to be outdated, but I'll link them anyway here):
<div>
<?php echo $this->partial('page_nav', array('var' => $this->var)) ?>
<div>
Obviously, your page_nav should be known by your view resolver.
I know this is old, but you can also simply call $this->render('path/to/view-script')
We have a actionSearchType in our User Controller as follows:
public function actionSearchType()
{
if (Yii::app()->user->isGuest == true)
$this->render('login');
else
$this->render('search_type');
}
Our actionLogin in our User Controller is as follows:
public function actionLogin()
{
$model= new Users();
// if it is ajax validation request
if(isset($_POST['ajax']))
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
$this->redirect(Yii::app()->user->returnUrl);
}
}
// display the login form
$this->render('login',array('model'=>$model));
}
The goal is to ensure that only authenticated users can execute the options on the search type view. When I run this page, I receive an error stating Undefined variable: model.
A snippet of the login view is as follows:
<div class="form">
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'login-form',
'enableClientValidation'=>true,
'clientOptions'=>array(
'validateOnSubmit'=>true,
),
)); ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<div class="row">
<?php echo $form->labelEx($model,'username'); ?>
<?php echo $form->textField($model,'username'); ?>
<?php echo $form->error($model,'username'); ?>
</div>
What steps must be taken to remedy the above error and properly check to ensure we have an authenticated user?
update
I changed actionSearchType to render the Login Widget per below:
public function actionSearchType()
{
if (Yii::app()->user->isGuest)
$this->widget('ext.LoginWidget');
else
$this->render('search_type');
}
This indeed resolved the error initially seen. A new problem is that there's no styling of the login widget when it renders. Should I echo my tags with appropriate stylesheet classes, or is there a bit more elegant way of doing that?
public function actionSearchType() {
if (Yii::app()->user->isGuest)
$this->redirect('/user/login');
$this->render('search_type');
}
Notes:
to do something when user is guest, simply use if(Yii::app()->user->isGuest) { statement }
to do something when user is logged in, simply use if(!Yii::app()->user->isGuest) { statement }
in the second code, public function actionLogin(), I think you have 2 more closing curly brackets than needed. Anyway, the login action should look like this:
public function actionLogin() {
$formModel = new Login_Form; // Login_Form.php should be in models folder
if (isset($_POST['Login_Form'])) {
$formModel->attributes = $_POST['Login_Form'];
if ($formModel->validate() && $formModel->login()) {
$this->redirect('/'); // replace / with stuff like Yii::app()->user->returnUrl
}
}
$this->render('login', array(
'formModel'=>$formModel,
));
}
Instead of rendering the view redirect to the user login page / action so you don't have to recreate it.
$this->redirect('login');
Somewhere in search_type you are referencing the variable $model which you do not hand over to the render() function. You need to define that variable otherwise the view will create an Exception.
I don't know which Model/Class your search_type view is expecting but you will need to initialize it before you hand it over to the view like this:
$this->render('search_type',array(
'model' => $model,
));
Here a good read about this topic: Understanding the view rendering flow
I have two modules in my installation. Both modules' controllers pass a variable called
$data['content']
to their views. Also, the first module's view runs the second module via
<?php echo Modules::run('module2'); ?>
and after that is supposed to display values from its $data['content'] variable. Unfortunately, that's when the first module's $data content is substituted with the second module's $data. This is pretty inconvenient for me, so I would like to know if there is a way to "protect" the $content variables and keep them only within their associated modules?
I would like to avoid renaming $data['content'] if possible. I've found a not-so-perfect solution in using
$data(__CLASS__)
but I am curious if it is possible not to change $data['content'].
class Some extends MX_Controller{
public function __construct(){parent::__construct();}
public function index(){
$this->load->view('template', array( //Primary template
'content' => 'some_index' // index view
));
}
public function _module_1(){
$this->load->view('module_1_view', array( //Module View : NO primary template
'' => '' <= no need to load view here, only data
));
}
public function _module_2(){
$this->load->view('module_2_view', array( //Module View : NO primary template
'' => '' <= no need to load view here, only data
));
}
}
-
template.php
<html>
<?php $this->load->view($content); ?>
</html>
-
some_index.php
<html>
//call modules
<?php echo Modules::run('some/_module_1'); ?>
<?php echo Modules::run('some/_module_2'); ?>
//call module from another class
<?php echo Modules::run('another_class/_module_1'); ?>
</html>
Given an html/javascript 'widget' which needs to have certain fields customized before use. For example, the css class ids need to be unique as the widget may appear more than once on the same page.
Let's say I want to keep the markup (js/html) of the widget stored as a template so that I can fill in the values that need to be customized during resuse.
I know that Zend Framework's views give you at least part of this functionality, but each view is generally associated with a particular controller. Given that this widget could be created from any controller, yes still needs to be able to access some properties stored in a controller (or model). Where should I put the widget markup and how then do I fill in the custom values?
Can I create a custom view that can be reused within the same page (appear more than once) as well as on other pages? If so, how do I set that up?
Sounds like you need a ViewHelper http://framework.zend.com/manual/en/zend.view.helpers.html. Create a custom helper that will fetch the data from a model and just simply output it. This way it won't depend on any controller, can be called in either the layout or in any view script. Example:
// views/helpers/Widget.php
class Zend_View_Helper_Widget extends Zend_View_Helper_Abstract
{
protected $_model = null;
protected $_view = null;
public function widget()
{
$data = $this->_getDataFromModel();
return $this->_view->partial('widget.phtml', array('data' => $data));
}
public function setView(Zend_View_Interface $view)
{
if($this->_view === null) {
$this->_view = $view;
}
return $this->_view;
}
protected function _getDataFromModel()
{
$this->_model = $this->_getModel();
return $this->_model->getDataForWidget();
}
protected function _getModel()
{
if($this->_model === null) {
$this->_model = new Model_Widget(); // or whatever it's called
}
return $this->_model;
}
The partial script:
// views/scripts/widget.phtml
<div class="widget-class"><?php echo $this->data; ?></div>
And when you need it in your views just call it like <?php echo $this->widget(); ?>
Note that I'm rendering the widget in a separate partial view script, just to avoid having html/css in the helper itself.
Hope this helps to get you started :)
Zend_View_Helper_Partial
Example:
<?php echo $this->partial('partial.phtml', array(
'css_id' => 'foobar')); ?>
To run this from any other module:
<?php echo $this->partial('partial.phtml', 'partials_module', array(
'css_id' => 'foobar')); ?>
In your partial view script (partial.html) you would then have access to $this->css_id.
What's the best way for constructing headers, and footers? Should you call it all from the controller, or include it from the view file? I'm using CodeIgniter, and I'm wanting to know what's the best practice for this. Loading all the included view files from the controller, like this?
class Page extends Controller {
function index()
{
$data['page_title'] = 'Your title';
$this->load->view('header');
$this->load->view('menu');
$this->load->view('content', $data);
$this->load->view('footer');
}
}
or calling the single view file, and calling the header and footer views from there:
//controller file
class Page extends Controller {
function index()
{
$data['page_title'] = 'Your title';
$this->load->view('content', $data);
}
}
//view file
<?php $this->load->view('header'); ?>
<p>The data from the controller</p>
<?php $this->load->view('footer'); ?>
I've seen it done both ways, but want to choose now before I go too far down a path.
Actually, after researching this quite a bit myself, I came to the conclusion that the best practice for including headers and footers in MVC is a third option - namely extending a base controller. That will give you a little more flexibility than the text's suggestion, particularly if you're building a very modular layout (not just header and footer, also sidebar panels, non-static menus, etc.).
First, define a Base_controller class, in which you create methods that append your page elements (header, footer, etc.) to an output string:
class Base_controller extends Controller
{
var $_output = '';
function _standard_header($data=null)
{
if (empty($data))
$data = ...; // set default data for standard header here
$this->_output .= $this->load->view('header', $data, true);
}
function _admin_header($data=null)
{
if (empty($data))
$data = ...; // set default data for expanded header here
$this->_output .= $this->load->view('admin_header', $data, true);
}
function _standard_page($data)
{
$this->_standard_header();
$this->_output .=
$this->load->view('standard_content', $data, true);
echo $this->_output; // note: place the echo statement in a
// separate function for added flexibility
}
function _page_with_admin_header($data)
{
$this->_admin_header($data);
$this->_output .=
$this->load->view('standard_content', $data, true);
echo $this->_output;
}
}
Then, in your page controllers, simply extend the base class and call your functions to build the page.
class Page_controller extends Base_controller
{
function index()
{
$data = ...; // Set content data here
$this->_standard_page($data);
}
function admin()
{
$data = ...; // Set content and header data here
$this->_page_with_admin_header($data);
}
}
Using a base controller, you can achieve very clean code in your individual page controllers AND have separate views for elements on the page (allowing code reuse in both views and controllers). All you need to do is define your common page 'sections' (what you might be tempted to call 'fragments') as functions in your base controller.
And if the base controller should start to grow uncontrollably (which can happen on large sites), you can rearrange some of its less-general functions by placing them in subclasses and letting the corresponding page controllers extend those instead of the original base controller.
Enjoy!
You could also try it this way -- define a default view template, which then pulls in the content based on a variable ('content' in my example) passed by the controller.
In your controller:
$data['content'] = 'your_controller/index';
// more code...
$this->load->vars($data);
$this->load->view('layouts/default');
Then define a default layout for all pages e.g. views/layouts/default.php
// doctype, header html etc.
<div id="content">
<?= $this->load->view($content) ?>
</div>
// footer html etc.
Then your views can just contain the pure content e.g. views/your_controller/index.php might contain just the variables passed from the controller/data array
<?= $archives_table ?>
<?= $pagination ?>
// etc.
More details on the CI wiki/FAQ -- (Q. How do I embed views within views? Nested templates?...)
I think the first way you are doing it is cleaner. Simply from a point of view of knowledge that is going to be rendered. Rather than having to enter the view file to find the rest.
It's bad practice to call views inside of other views. This could be a form of controller view mixing. The view function in CI allows you to pass a third parameter that causes it to return that view's output as a string. You can use this to create a compound view.
For example:
class Page extends Controller {
function index() {
$data['page_title'] = 'Your title';
$this->load->view('default_layout', array(
'header' => $this->load->view('header' , array(), true),
'menu' => $this->load->view('menu' , array(), true),
'content' => $this->load->view('content', $data , true),
'footer' => $this->load->view('footer' , array(), true),
));
}
}
default_layout.php
<? echo $header, $menu, $content, $footer; ?>
You may want to combine your header and footer to make a template like this.
class Page extends Controller {
function index() {
$data['page_title'] = 'Your title';
$this->load->view('default_template', array(
'menu' => $this->load->view('menu' , array(), true),
'content' => $this->load->view('content', $data , true),
));
}
}
default_template.php
<html><head></head><body><span>Some Header HTML</span> // this is your header html
<? echo $menu, $content; ?>
<span>some footer HTML</span></body></html> // this is your footer html