rendering different view engine in zend depending on extension - php

I have a normal phtml template and another one in HAML. So somewhere in my Bootstrap.php:
protected function _initView()
{
$view = new Haml_View();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
return $view
}
I initialize to use my Haml_View, but what i want is if the script filename has an extension .haml, it'll use Haml_view if not, then it'll use the regular Zend_view.
So i guess my question is, is there any way to find out what the current view script filename will be use?
Thanks

The basic workflow of a ZF MVC request is as follows:
Application bootstrapping
Routing
Dispatch
Zend_Application takes care of only the first item in that list,
bootstrapping. At that time, we have no idea what the request actually
is -- that happens during routing. It's only after we have routed that
we know what module, controller, and action were requested.
Source: http://weierophinney.net/matthew/archives/234-Module-Bootstraps-in-Zend-Framework-Dos-and-Donts.html
So you can't switch the view class based on script suffix in the bootstrap because the routing has not occured yet. You could do it in a FrontController plugin as early as routeShutdown, but I feel it's more natural to do it in an Action Helper. The normal methods to figure out the view script path are in Zend_View and Zend_Controller_Action_Helper_ViewRenderer. Both of these are easily available in an Action Helper.
Zend_Controller_Action_Helper_ViewRenderer is also an Action Helper and it needs to init before, so let's do our switch after the init, in the preDisptatch call of an Action Helper.
First, you need to register your helper. A good place is in the bootstrap with your view:
protected function _initView()
{
$view = new Haml_View();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
return $view;
}
protected function _initHelpers()
{
Zend_Controller_Action_HelperBroker::addHelper(
new Haml_Controller_Action_Helper_ViewFallback()
);
}
And the helper would look like this:
class Haml_Controller_Action_Helper_ViewFallback extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
/** #var $viewRenderer Zend_Controller_Action_Helper_ViewRenderer */
$viewRenderer = $this->getActionController()->getHelper('ViewRenderer');
/** #var $view Haml_View */
$view = $viewRenderer->view;
/**
* what i want is if the script filename has an extension .haml,
* it'll use Haml_view if not, then it'll use the regular Zend_view
*/
$viewRenderer->setViewSuffix('haml');
$script = $viewRenderer->getViewScript();
if (!$view->getScriptPath($script)) {
$viewRenderer->setView(new Zend_View());
$viewRenderer->setViewSuffix('phtml');
$viewRenderer->init();
}
}
}
If there is no file with the haml extension in the default path, we assume there is one with phtml extension, and we modifiy the ViewRenderer accordingly. Don't forget to init the ViewRenderer again.

Something along the lines of:-
if(!file_exists('path/to/view.haml'){
$view = new Zend_View();
$viewRenderer->setView($view);
}
May work, although I haven't tested it.
Edit:
You could try this in your controller:-
class IndexController extends Zend_Controller_Action {
public function init()
{
$paths = $this->view->getScriptPaths();
$path = $paths[0];
$script = $path . $this->getHelper('viewRenderer')->getViewScript();
if(!file_exists($script)){
$this->viewSuffix = 'hmal';
}
}
public function indexAction()
{
}
}
I'm not 100% happy with the $path = $paths[0] bit, but I don't have time to look into it any further. Hopefully that will point you in the right direction.

Related

Calling various methods of a controller from the loader

I am trying to build a loader to load various controller with their methods (as needed) within a controller. I sketched out a simple code on my home controller to call the LeftController (now a dummy controller but I intend to use this controller as menu).
require 'controller/LeftController.php';
$LeftController = new LeftController();
$LeftController->index();
This works within the HomeController. It loads the LeftController controller and displays the method index().
Basing my Loader on the above code this is what I have done till now
class Loader
{
public function controller($controller)
{
$file = 'controller/' . $controller . '.php';
$class = $controller;
if (file_exists($file)) {
require($file); // require 'controller/LeftController.php';
$controller = new $class(); //$LeftController = new LeftController();
var_dump($controller);
}
}
}
This works too and the controller is instantiated. I see result using the var_dump().
Now, I need to call the method, as we see at the top most code $LeftController->index(); but on the Loader class this time.
One way of doing this is if I add $controller->index() right after the $controller = new $class(); but this will always call the index() method of the controller.
How do I code this method part as such that I can call any method associated with the controller and not just the index().
You can pass a method argument with your controller:
public function controller($controller, $method)
and then call it on your newly created object:
$controller->$method()
However,
it seems you are trying to reinvent the wheel. The part where you verify if a files exists, include it and instantiate the class, is called autoloading.
The code could look like this:
public function controller($controller, $method)
{
$instance = new $controller();
return $instance->$method();
}
While the autoloading part makes use of spl_autoload_register() to manage finding and including files.
The spl_autoload_register() function registers any number of autoloaders, enabling for classes and interfaces to be automatically loaded if they are currently not defined.
So you can use the code you already have, and abstract it from the action of instantiating the class:
spl_autoload_register(function autoloader($controller) {
$file = 'controller/' . $controller . '.php';
if (file_exists($file)) { require($file); }
});

codeigniter load model in core folder

I'm new with CodeIgniter and I'd like to load a model in core/MY_Lang.php class.
class MY_Lang extends CI_Lang
{
/** array $languages Array of languages */
var $languages = array('');
/** array $special Special URIs (not localized) **/
var $special = array('');
/** string $default_uri Where to redirect if no language in URI */
var $default_uri = '';
public function getLanguages()
{
$this->load->model('Lang_model');
return $this->Lang_model->GetLangs();
}
Of course this is just an example of what I'm trying to achieve. It doesn't work and my questions are:
is it a good practise do something like this?
if yes, how can I load my model in that class?
i didn't load most of core classes of CI .
the frequently used class , we load theme in autoload.php file instead of load them every time .

Is there a way to display the original content of a twig template once it is loaded, before rendering anything?

Does someone know if there is a way to display the original raw content of a twig template once it is loaded, before rendering anything ?
Let's say that I've got a root template:
{# templates/template.txt.twig #}
This is my root template
{{arg1}}
{{ include('other/internal.txt.twig') }}
and another one included from the root one:
{# templates/other/internal.txt.twig #}
This is my included template
{{arg2}}
If I render template.txt.twig with arg1='foo' and arg2='bar', the result will be
This is my root template
foo
This is my included template
bar
It there a way to retrieve the loaded template before any variable evaluation ?
What I expect is to get something like this:
This is my root template
{{arg1}}
This is my included template
{{arg2}}
The idea behind that is to leverage all twig loading mechanism (loading path, namespaces...)
because I need to make some custom automatic checks on the twig code itself (not related to twig syntax but with the consistency of a high level model not related to twig)
Let me know if this makes sense or if you need more information
Thank you for your help
To render a template, Twig compiles the Twig content into PHP code so I don't think this is possible with Twig itself. You can have a look at the doc or at the presentation of Matthias Noback that Thierry gave you the link.
The only solution I think you have is to read the file with file_get_contents but you won't have the included template in the right place as in your exemple.
If you are wanting to process the template with some kind of service before rendering it then you could override the twig environment and run your checks in the loadTemplate method.
For example here I will inject a templateValidator and then run that on each template load.
App\AcmeBundle\Twig\Twig_Environment
First extend \Twig_Environment and override the loadTemplate method as well as add a setter for injecting the templateValidator.
In the loadTemplate I have replaced instances of $this->getLoader()->getSource($name) with $this->validateTemplate($name) which does the same thing but also before what ever action you wish to add (in this case $this->templateValidator->validate($source).
namespace App\AcmeBundle\Twig;
use \Twig_Environment as BaseEnvironment;
class Twig_Environment extends BaseEnvironment
{
protected $templateValidator;
public function setTemplateValidator(TemplateValidator $templateValidator)
{
$this->templateValidator = $templateValidator;
}
public function loadTemplate($name, $index = null)
{
$cls = $this->getTemplateClass($name, $index);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
}
if (!class_exists($cls, false)) {
if (false === $cache = $this->getCacheFilename($name)) {
//eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name));
eval('?>'.$this->compileSource($this->validateTemplate($name), $name));
} else {
if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
//$this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
$this->writeCacheFile($cache, $this->compileSource($this->validateTemplate($name), $name));
}
require_once $cache;
}
}
if (!$this->runtimeInitialized) {
$this->initRuntime();
}
return $this->loadedTemplates[$cls] = new $cls($this);
}
/**
* Validate template and return source
*
* #param string $name
* #return string
*/
private function validateTemplate($name)
{
$source = $this->getLoader()->getSource($name);
if (null !== $this->templateValidator) {
$this->templateValidator->validate($source);
}
return $source;
}
}
app/config/config.yml
Override the parameter twig.class so DI uses your class rather than the original \Twig_Environment.
parameters:
twig.class: App\AcmeBundle\Twig\Twig_Environment
App\AcmeBundle\DependencyInject\Compiler\TwigEnvironmentInjectCompilerPass
Create a compiler class to inject the templateValidator into your newly created TwigEnvironment (only if it has the 'setTemplateValidator' method so it degrades properly)
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class TwigEnvironmentInjectCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('twig') ||
!$container->hasDefinition('acme.template_validator'))
{
return;
}
$twig = $container->getDefinition('twig');
if (!$twig->hasMethodCall('setTemplateValidator')) {
return;
}
$twig->addMethodCall(
'setTemplateValidator',
array(new Reference('acme.template_validator'))
);
}
}
App\AcmeBundle\AppAcmeBundle
Add your compiler pass to the bundle build
use App\AcmeBundle\DependencyInject\Compiler\TwigEnvironmentInjectCompilerPass;
class AppAcmeBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new TwigEnvironmentInjectCompilerPass());
}
}
Note This isn't tested in any way, it's all just off the top of my head, so it could all be wrong.
I believe the Elao/WebProfilerExtraBundle available at :
https://github.com/Elao/WebProfilerExtraBundle
might provide you with the additional details you're seeking in the aforementioned example.
See also:
http://qpleple.com/webprofilerbundleextra-a-must-have-tool-for-symfony2-developers/
http://www.slideshare.net/matthiasnoback/diving-deep-into-twig

How to make variable that can be used in many views?

I have a project, it has a structure like this:
http://img526.imageshack.us/img526/2333/92348689.png
I want to make a variable like the following
$templatePath = $this->baseUrl('/application/templates/')`
and it can be used in many views, in many modules. I think I can do it by declaring the variable in Bootstrap.php (application) but I don't know how to do that.
Usually, I just place myself such variables into application bootstrap file. Here's an example:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
protected function _initViewVariables() {
$this->bootstrap('View');
$this->view->fooValue = 'foo';
$this->view->barValue = 'bar';
$config = Zend_Registry::get('config');
if( $config->recaptcha->enabled ) {
$this->view->captcha = true;
$this->view->recaptchaPublicKey = $config->recaptcha->publicKey;
}
else {
$this->view->captcha = false;
}
}
}
I hope it helps!
Base Url is available after routing has been completed (routeShutdown hook) so accessing it in Bootstrap will not work .
So create a controller plugin inside preDispatch() do
public function preDispatch($req) {
$view = new Zend_View();
$view->placeholder('burl')->set(Zend_Controller_Front::getInstance()->getBaseUrl());
}
To access it inside view do like index.phtml
echo $this->placeholder('burl');
You could use the Zend_Registry.
In your bootstrap or wherever in your site just State
Zend_Registry::set("TagLine", "Have a Nice Day");
To use in a view just
<?= Zend_Registry::get("TagLine"); ?>
for extra credit you could also make a view helper for this (there is one with ZF2)
class My_View_Helper_Registry extends Zend_View_Helper_Abstract
{
public function registry($key)
{
return Zend_Registry::get($key);
}
}
In your bootstrap you will add a method like:
protected function _initSiteRegistry(){
Zend_Registry::set("Site_Name", "My Cool Site";
Zend_Registry::set("Site_TagLine", "Have a nice day";
}
Also if you are using the view helper approach you will also want to register the helper path.. you can do this in _initView in the bootstrap.
protected function _initView(){
$view = new Zend_View();
$view->doctype("HTML5");
$view->setEncoding("UTF-8");
$view->addScriptPath(APPLICATION_PATH."/local/views");
// set this as the viewRenderer's view.
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setView($view);
$view->addHelperPath("My/View/Helper/", "My_View_Helper_");
return $view;
}

How do I pass parameters to my Zend Framework plugins during instantiation?

I have defined a plugin on the libraries path, using the correct directory structure, and have made it's presence known in the application.ini file. The plugin loads, and my preDispatch() method fires. But, how can I pass parameters to the plugin during instantiation?
Here is my code:
class Project_Controller_Plugin_InitDB extends Zend_Controller_Plugin_Abstract {
private $_config = null;
public function __contruct($config){
$this->_config = $config;
}
public function preDispatch(Zend_Controller_Request_Abstract $request) {
$db = Zend_DB::factory("Pdo_Mysql", $this->_config);
Zend_Registry::set("db", $db);
}
}
Specifically, how do I pass $config to the __construct() method?
Thanks,
Solution
Here is what I ended up with (Thanks to Phil Brown!):
In my application.ini file:
autoloaderNamespaces[] = "MyProjectName_"
In my Bootstrap.php file:
protected function _initFrontControllerPlugins() {
$this->bootstrap('frontcontroller');
$frontController = $this->getResource('frontcontroller');
$plugin = new MyProjectName_Controller_Plugin_InitDB($this->_config->resources->db->params);
$frontController->registerPlugin($plugin);
}
Simply register your plugin manually in your Bootstrap class
protected function _initFrontControllerPlugins()
{
$this->bootstrap('frontcontroller');
$frontController = $this->getResource('frontcontroller');
// Config could come from app config file
// or anywhere really
$config = $this->getOption('initDb');
$plugin = new Project_Controller_Plugin_InitDB($config);
$frontController->registerPlugin($plugin);
}
Use Zend Registry
Have you read this? $Config structure resembles Zend_Config very closely, so if you wish to pass extra parameters, treat it as an array of key/values.

Categories