How to move the "views" of Zend_Layout - php

Normally it would be in such a structure:
../application/modules/somemodule/views/scripts/index/index.phtml
How I move it to:
../application/templates/somemodule/template1/views/......
../application/templates/somemodule/templateTWOOMG/.......
??

You need to play with: $viewRenderer::setViewBasePathSpec();
For example in frontController plugin, (or easier, but not so flexible, in Bootstrap.php):
$templateName = 'myTemplate';
$bootstrap = $this->getBootstrap();
$bootstrap->bootstrap('layout');
if ($bootstrap->hasResource('layout')) {
$layout = $bootstrap->getResource('layout');
$layout->setLayoutPath($basePath . '/layouts/scripts/');
}
$bootstrap->bootstrap('view');
if ($bootstrap->hasResource('view')) {
$view = $bootstrap->getResource('view');
} else {
$view = new Zend_View;
}
$vr = Zend_Controller_Action_HelperBroker::getExistingHelper("viewRenderer");
$vr->setViewBasePathSpec($basePath."/modules/:module/$templateName/views/");
Take a look on getters and setters in frontController, view, layout and viewRenderer classes. There are plenty of methods which allow to customize default directory structure.

I did it with a plugin, and set a variable in my config to specify the name of the theme.
class Application_Plugin_ThemeSetup extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// load up the config and the view object
$objConfig = Zend_Registry::get('config');
$objView = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
// set path for views based on theme designation in config
$theme = ! empty($objConfig->theme->name) ? $objConfig->theme->name : 'default';
$Renderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$Renderer->setViewBasePathSpec(APPLICATION_PATH."/views/$theme");
// add some variable to the view at high level
$objView->themeName = $objConfig->theme->name;
$objView->themeDescription = $objConfig->theme->description;
}
}

Related

Dynamic file system class to automatically create an instance of a specific class (in PHP and PhpStorm)

I've created a function that loads a specific class and also should create a new instance of this class:
class File {
public static function load($file, $folder = null, $local = false, $classname = null) {
if ($folder == 'root') {
$dir = '';
} elseif ($folder) {
$dir = $folder . '/';
} else {
$dir = 'classes/';
}
if ($local) {
$root = ROOT_ABS;
} else {
$root = ROOT_CMS_ABS;
}
require_once $root . $dir . $file . '.php';
// New Instance
if ($folder == null || $classname) {
if (!$classname) {
return new $file();
} else {
return new $classname();
}
}
}
I've built in some extra parameters to specify if my CMS folder should be accessed (ROOT_CMS_ABS AND ROOT_ABS constants). So the minimum default case to load a file and also create an object is e.g.:
$html = File::load('html');
Issue:
I'm using PhpStorm and usually I could hold Ctrl + Click to jump to a function of an object.
But now PhpStorm can't recognize which class has been loaded. In my example this is because I assign $router to this class instead of writing $router = new AltoRouter();.
Question:
1) Is there another way that PhpStorm recognized which class I'm referring to?
2) I'm kinda new to OOP in PHP, could I improve my class somehow?
__
Otherwise the function works fine. So this is more likely an IDE issue.
You can easily solve it by adding inline PHPDoc comment with type hint before such assignment. For example:
/** #var MyClassName $router */
$router = File::load('AltoRouter', 'route');
Other than that: using $router = new AltoRouter(); is much better -- leave actual class loading for class autoloading mechanism (following PSR-4; be it standard these days Composer .. or your own implementation).

PHP & MVC: Trouble dynamically creating appropriate MVC Classes

I'm trying to build a CMS using the MVC pattern. The structure of my CMS is as follows:
->index.php: entry point. Contains the appropriate includes and creates the frontController which takes a router as it's parameter:
$frontController = new FrontController(new Router);
echo $frontController->output();
->router.php: analyses the URL and defines what the names of each Model,View and Controller will be. By default they are preceded by home, but if the url is of the form http://localhost/index.php?route=Register the MVC classes will be named RegisterModel, RegisterView and Register Controller.
if(isset ( $_GET ['route'] ))
{
$this->URL = explode ( "/", $_GET ['route'] );
$this->route = $_GET ['route'];
$this->model = ucfirst($this->URL [0] . "Model");
$this->view = ucfirst($this->URL [0] . "View");
$this->controller = ucfirst($this->URL [0] . "Controller");
}
else
{
$this->model = "HomeModel";
$this->view = "HomeView";
$this->controller = "HomeController";
$this->route = "Home";
}
->frontController.php: This is where I am stuck. When I go to the homepage, it can be visualised correctly because I already have the default HomeModel,HomeView and HomeController classes created. But I created a link that points to register (localhost/index.php?route=Register) but the PHP log indicates that the appropriate Register classes weren't created by the frontController class.
class FrontController
{
private $controller;
private $view;
public function __construct(Router $router)
{
$modelName = $router->model;
$controllerName = $router->controller;
$viewName = $router->view;
$model = new $modelName ();
$this->controller = new $controllerName ( $model );
$this->view = new $viewName ( $router->getRoute(), $model );
if (! empty ( $_GET['action'] ))
$this->controller->{$_GET['action']} ();
}
public function output()
{
// This allows for some consistent layout generation code
return $this->view->output ();
}
}
At this moment I have no idea how to go about solving this issue. And even if I get the classes to be created in the frontController, is there a way to specify that the classes being dynamically generated should extend from a base Model,View,Controller class?
The default HomeView.php looks like this:
class HomeView
{
private $model;
private $route;
private $view_file;
public function __construct($route, HomeModel $model)
{
$this->view_file = "View/" . $route . "/template.php";
echo $this->view_file;
$this->route = $route;
$this->model = $model;
}
public function output()
{
require($this->view_file);
}
}
Any indications on anything that might help me get unstuck or a pointer in the right direction would be much appreciated.
EDIT1:
I forgot to add a summary of my two issues:
1. I would like to understand why the classes aren't being created in the FrontController class...
2. Once created how would I access those classes? Answer is in the comment section. Using the PHP spl_autoload_register function.
Thanks All!

Twig renders / display only the view path

I'm creating an mvc structure for learning/teaching purpouses and so far I could set up the structure and a controller plus twig as template system.
The structure is:
index.php
controllers/
error.php
inc/
controller_base.php
view_manager.php
views/
.cache/
error/
view.html
So:
index instantiate twig autoloader (and mvc autoloader by spl_register).
index instantiate error controller inheriting controller_base.
controller_base is holding the view_manager.
error call view_manager to display the error/view.html and the only thing I get on the browser is error/view.html.
No errors on the apache log. (error_reporting(E_ALL))
Twig cache files created correctly, but the content doesn't look good to me:
protected function doDisplay(array $context, array $blocks = array()) {
// line 1
echo "error/view.html";
}
Anyone knows why, and how can I print the actual view?
Thanks in advance.
Code:
index.php: Declaring autoloaders
function __autoload($class_name)
{
if(file_exists("controllers/$class_name.php")):
include strtolower("controllers/$class_name.php");
elseif(file_exists("models/$class_name.php")):
include strtolower("models/$class_name.php");
elseif(file_exists("inc/$class_name.php")):
include strtolower("inc/$class_name.php");
endif;
}
spl_autoload_register('__autoload');
require_once 'vendor/autoload.php';
Twig_Autoloader::register(); has been avoided cause Twig installation was done by composer.
Adding it doesn't bring any change.
error.php (controller): called method.
public function show($param)
{
$this->viewMng->display(get_class().$data['view'], array())
}
controller_base.php:
class base
{
protected $viewMng;
public function __construct()
{
$this->viewMng = new viewmanager();
}
}
viewmanager.php: whole class
class viewmanager {
private $twig;
protected $template_dir = 'views/';
protected $cache_dir = 'views/.cache';
// protected $vars = array();
public function __construct($template_dir = null) {
if ($template_dir !== null) {
// Check here whether this directory really exists
$this->template_dir = $template_dir;
}
$loader = new Twig_Loader_String($this->template_dir);
$this->twig = new Twig_Environment($loader, array(
'cache' => $this->cache_dir));
}
public function render($template_file, $data = array()) {
if (!file_exists($this->template_dir.$template_file)) {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
return $this->twig->render($template_file, $data);
}
public function display($template_file, $data) {
if (!file_exists($this->template_dir.$template_file)) {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
$tmpl = ($this->twig->loadTemplate($template_file));//print_r($tmpl);
$tmpl->display($data);
}
}
view.html:
<html><body> Hello </body></html>
The problem is based on the loader.
According to the Twig documentation:
Twig_Loader_String loads templates from strings. It's a dummy loader
as the template reference is the template source code This
loader should only be used for unit testing as it has severe
limitations: several tags, like extends or include do not make sense
to use as the reference to the template is the template source code
itself.
That's why it only prints the passed in string.
Twig_Loader_String, should be replaced by the proper loader.
In this case it works perfectly well Twig_Loader_Filesystem.
$loader = new Twig_Loader_Filesystem($this->template_dir);
This solve the problem and the MVC structure works completely fine.
Thanks taking a look, guys.

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