Well, this is a tricky one, and I'm not really sure it's not breaking the MVC model.
I'm loading some data into the controller, retrieved from the model. I'm passing this object to the view almost in every action. I'm processing this data from a helper and I'm passing the object as an argument:
controller:
$this->('section', $section);
helper:
<h3><?php echo $parser->section_name($section); ?></h3>
However, I think it would be way better if I could pass that $section object as a private variable inside the parser helper. I could do this in the first line of each view:
$parser->section_object = $section;
And each parser method will look something like
function section_name(){
return $this->section_object['Section']['name'];
}
The question is: is there a way to automatizate this from the controller? Because the controller can't access the helper, I tried creating the helper from the controller and setting the local variable there:
function beforeFilter(){
$section = $this->Section->getOne();
App::import('Helper', 'Parser');
$ParserHelper = new ParserHelper();
$ParserHelper->section_object = $section;
$this->set('parser', $ParserHelper);
}
However, if the helper includes some other helpers, they won't be loaded and the helper will trigger a lot of errors.
Thanks.
You have to manually create the helpers used by your helper. For example, if your helper uses the HtmlHelper, you have to do something like:
App::import('Helper', 'Html');
$ParserHelper->Html = new HtmlHelper();
Related
I want to plug twig into an application that would need some session-based data incorporated in the base. For example, the client's current timezone shows in the footer. It doesn't make sense for the individual controllers to know about this, since it has nothing to do with them; but on the other hand, they select and populate the view:
class MyController
{
public function index()
{
$template = $this->twig->loadTemplate('myPageTemplate.html.twig');
return $template->render($dataArray);
}
}
Is there some well-formed way to pass a data object to twig before you select a view, and make that available to the base template? Something that you would do upon firing up the Twig_Environment and passing it in?
Session variables need to be set in a controller. As can be seen in the docs, it would look something like this:
public function indexAction(Request $request)
{
$session = $request->getSession();
// store an attribute for reuse during a later user request
$session->set('foo', 'bar');
// get the attribute set by another controller in another request
$foobar = $session->get('foobar');
// use a default value if the attribute doesn't exist
$filters = $session->get('filters', array());
}
Those variables are then easily passed when rendering a template with something like:
return $this->redirect($this->generateUrl('home', array('foobar' => $foobar)));
If you don't want all of your controllers to deal with the injection of those "global" variables, you can implement a base controller class that all other controllers inherit from and do the following inside:
public function render($view, array $parameters = array(), Response $response = null)
{
if(!isset($parameters['timezone'])) {
// fill the parameter with some value
$parameters['timezone'] = $this->getSession()->get('timezone');
}
return parent::render($view, $parameters, $response);
}
This allows you to do a "global" injection without giving control completely away from the controllers.
Don't forget to make your base controller extend Symfony\Bundle\FrameworkBundle\Controller\Controller.
The best approach would be adding a Twig extension so you don’t have to include anything in your controller. With the extension you can either add a global as someone already suggested (http://twig.sensiolabs.org/doc/advanced.html#globals) or functions (http://twig.sensiolabs.org/doc/advanced.html#functions) which you can then use directly (like {{ user_timezone() }} ).
Here is a simple view helper (notice the pass-by-reference argument):
class Zend_View_Helper_MyViewHelper extends Zend_View_Helper_Abstract
{
public function MyViewHelper(&$array)
{
unset($array['someExistingKey']);
}
}
This does not work in the view. $array['someExistingKey'] is still set (except within the immediate context of the method). Zend must be doing something to prevent the array from being passed in by reference. Any ideas on a solution?
When you call $this->MyViewHelper($array) from your templates you are not actually calling the helper class directly, Zend_View is instantiating the class and calling it for you. So I think you might have trouble getting this working. Your best bet is probably to use Zend_Registry, or refactor to take a different approach not requiring a global.
I just thought of a workaround. You just have to call the helper manually, instead of letting ZF call it through call_user_func_array.
Ref.php
class Zend_View_Helper_Ref extends Zend_View_Helper_Abstract
{
public function removeFromRef(&$ref)
{
// change your var value here
unset($ref['key']);
}
/**
* ZF calls this for us, but we'll call what we want, so you can skip this.
*/
// public function ref()
// {}
}
As you can see, you can skip the convention of having to name your main method as the filename, but I still recommend it.
Now, you can pass references in views/controllers:
// in view:
$this->getHelper('Ref')->removeFromRef($someVar2Change);
// in controller
$this->view->getHelper('Ref')->removeFromRef($someVar2Change);
Basically, this is what $this->ref() does: gets the helper, then calls call_user_func_array.
Some people may have problems using $this->getHelper('Ref')->ref() instead of $this->ref() though, but it works.
I know how to pass variable from controller into a view:
$this->render('view_name', array('variable_name'=>'variable_value'));
however I'd like to pass some variables to layout. The only connection between controller and layout seems to be the public $layout attribute in controller class, like this:
public $layout='//layouts/column2';
However, I do not see a way to pass a variable to it?
Alternatively, you could add a property in the Controller such as
class SiteController extends CController {
public $myvar;
//...
And then output it in the layout (//layouts/column2)
echo isset($this->myvar) ? $this->myvar : '';
It doesn't really seem to be set up to do that easily from what I can tell, so if you are relying on it to pass a lot of data, you might want to think of a different way to set up your application.
A couple ways that you could do it are to use the Yii params via
Yii::app()->params['myvar'] = $mixed;
which you can set in the controller and access in the layout. Otherwise you can use regular PHP global vars, with all the issues that approach entails.
In your controller you would do something like:
global $testvar;
$testvar = 'hello';
and in the layout:
echo $GLOBALS['testvar'];
(Even if it's not in a function, you still need to retrieve it via GLOBALS.)
You could pass an object this way for more structured data, but you are still using a global var. Another, possibly even less desirable method, would be via a session var, e.g., Yii::app()->session['myvar'] or a Yii "flash message".
in controller pass the variable, then in VIEW (not layout yet)
create
$this->params['myvar'] = 'hello';
Now in layout you can access whole array with just
echo $this->params['myvar'];
Hope this helps you.
After sets of debugging in Yii2 I found out that the only variables (excluding global variables) that are accessible inside of a layout file are _file_ (path to current layout file) and _params_ (an array containing variable content that is a HTML output bufferized from a file passed for rendering from a controller).
Except answers provided by #ldg (which I consider as most useful and informative, but resource spending) and #Petra Barus.
I also came out with a good solution of dividing layout into explicit files and calling them inside of a rendered file:
echo $this->renderPhpFile(Yii::getAlias('#app/views/layouts/somelayout.php'), [
'var' => $variableThatIsAccessibleInRenderedFile,
]);
From your controller you can do something like this:
$this->render('/mail/registration',array('url'=>$url, 'description'=>'some description'));
and access the variables from your view like this:
<h3><?php echo $url; ?></h3>
and here is your answer; you can access these same variables from the layout like this:
<h3><?php echo $data['url']; ?></h3>
I'm adding new features to an existing code base. Anyway, the current feature I'm doing should be in MVC in my opinion. The existing code base isn't MVC but the feature I'm implementing, I want it to be MVC. And I don't want to roll some existing MVC into the existing codes.
So, my problem is... I don't know to implement a render function for the controller class. Usually, in MVC you have the controller do some stuff, set it to a variable using a render function, and the View can now magically access that given variable set by the controller.
I have no idea how to do this other than global, which just feel wrong, I keep on telling myself there has to be a better way. Edit: It's global isn't it? >_> How does those other frameworks do it?
Here's a silly example:
Controller:
class UserController extend BaseController
{
public function actionIndex()
{
$User = new User; // create a instance User model
$User->getListofUser();
$this->render('ListOfUser', 'model'=>$model);
}
}
View:
<?php
//I can use $ListOfUser now...
//some for loop
echo $ListofUser[$i];
?>
Thank you in advance!
A very simple example:
class View {
function render($file, $variables = array()) {
extract($variables);
ob_start();
include $file;
$renderedView = ob_get_clean();
return $renderedView;
}
}
$view = new View();
echo $view->render('viewfile.php', array('foo' => 'bar'));
Inside viewfile.php you'll be able to use the variable $foo. Any code in the view file will have access to $this (the View instance) and any variables in scope inside the render function. extract extracts the array's contents into local variables.
I have a view helper that manages generating thumbnails for images. The images are stored using a unique ID and then linked to a file resource in the database.
I am trying to find out if it is possible for the view helper that generates these images to access the model or controller directly, as it is not possible to load the image data at any other point in the controller work flow.
I know this is a bit of a hack really, but it is easier than trying to rebuild the entire data management stack above the view.
If you had set the data in the model or controller you could access it. So you'd have to think ahead in the controller. As you said you can't load it in the controller, perhaps you need to write a specific controller function, which you can call from the view using $this->requestAction() and pass in the image name or similar as a parameter.
The only disadvantage of this is using requestAction() is frowned upon, as it initiates an entirely new dispatch cycle, which can slow down your app a bit.
The other option, which may work is creating a dynamic element and passing in a parameter into the element and have it create the image for you. Although I'm not too sure how this would work in practise.
How are you generating the thumbnails using the helper in the view if you aren't passing data into it from a controller or model? I mean if it was me, I would be setting the 'database resource' in the controller, and passing it to the view that way, then having the helper deal with it in the view. That way you could bypass this issue entirely :)
$this->params['controller'] will return what you want.
According to the ... you can put this code in a view.ctp file then open the URL to render the debug info:
$cn = get_class($this);
$cm = get_class_methods($cn);
print_r($cm);
die();
You could write a helper and build in a static function setController() and pass the reference in through as a parameter and then store it in a static variable in your helper class:
class FancyHelper extends FormHelper {
static $controller;
public static function setController($controller) {
self::$controller = $controller;
}
... more stuff
}
Then in your Controller class you could import the FancyHelper class and make the static assignment in the beforeFilter function:
App::uses('FancyHelper', 'View/Helper');
class FancyController extends AppController {
public $helpers = array('Fancy');
function beforeFilter() {
FancyHelper::setController($this);
}
... more stuff
}
And then you could access the controller from other public functions inside FancyHelper using self::$controller.
You can check the code(line ☛366 and
line ☛379) of the FormHelper, try with:
echo $this->request->params['controller'];
echo Inflector::underscore($this->viewPath);