I have been pouring over Zend_View and the various classes and interfaces that make up the view. One thing I am attempting to replicate in a project that does not use zend in any way shape or form is the:
$this->view->variable = 'Hello world';
that you can set in a controller and then do:
echo $this->view->variable;
My ultimate goal is to do something like:
$this->variable = new SomeClass
and then else where, in a view specifically, do:
$this->variable->someMethod();
My question is:
How would I replicate what zend does to do something simmilar with out using global variables?
How is zend able to do something like $this->view with out ever instantiating or saying what view is?
this would help me understand how, variables are passed around or objects are passed from the logic to the view and how php allows for something like $this->view to work when in a view or not.
note: this is not a Zend specific question and "use zend" is not the answer. I am looking to replicate a specific feature. My project does not in any way use or affiliate with zend.
I don't know why exactly you want to achieve this, but as a super simple setup (which is by no means suited to be the basis of an MVC framework) you can look at this:
<?php
class Controller
{
private $view = null;
public function __construct()
{
$this->view = new View();
$this->view->someVar = "foobar";
}
public function render()
{
include "view.php";
}
}
class View
{
}
$controller = new Controller();
$controller->render();
And then, in view.php, you can do:
<?php
echo $this->view->someVar;
Beware: This code only shows HOW it's possible to achieve such a construct, it does not anything useful at all ;).
It's actually pretty simple. When you use include, require, eval et al., the loaded code is brought in to the current scope. So if you have a template file
template.php
<span><?=$this->view->somevar?></span>
Controller.php
<?php
class Controller
{
private $view;
public function doSomething()
{
$this->view->somevar = 'Hello World';
include 'template.php';
}
}
index.php
<?php
require 'Controller.php';
$oC = new Controller();
$oC->doSomething();
Blamo.., template.php is able to call $this->view->somevar as it is treated as part of Controller.php. Running php index.php on the CLI produces
<span>Hello World</span>
To elaborate a tiny bit, if $this->view inside of Controller.php were a class you've defined rather than a simple instance of stdClass as in the above demonstration, and it had a function someMethod, you could call $this->view->someMethod() from template.php just the same.
Related
I'm currently creating a framework, just for the sake of a personal study-project, i.e. to be able to evaluate php-frameworks in general futurewise. As I'm starting to build up more appreciation for some of the frameworks, especially about the way they designed their modules and made them available in 'their scope', during the development of this project I'm also baffled as to why almost all framework-developers include the (view)templates inside the scope of their classes.
Perhaps there are some developers roaming these boards that are able to be constructive about it.
An example (which for the sake of clarity is very much oversimplified).
Why do this...
<?php
class TemplateInc {
public $template;
public function __construct($page) {
$this->template = $page;
}
public function render() {
require_once $this->template;
}
}
$template_include = new TemplateInc('mypage.html');
$template_include->render();
?>
...when its also possible to do it like this, at the same time being able to also use the global-scope variables in the templates (if appropriate) ?
<?php
class TemplateName {
public $template;
public function __construct($page) {
$this->template = $page;
}
public function getTemplate() {
return $this->template;
}
}
$templatename = new TemplateName('mypage.html');
require_once $templatename->getTemplate();
?>
Usually the template class would also make additional values available to the template. E.g.:
public function render(array $vars) {
extract($vars);
require $this->template;
}
Or the template could make use of $this->.. to access other methods/objects/values/services which are part of the class. None of this is possible if you do the require outside of the class. Then only global variables are available to the template, of which you hopefully don't have any (don't use global). Doing all this inside a method allows you do to something like extract($vars) without cluttering the global scope.
Further, instead of a nicely self-contained call to $template->render(), you now need to do require $template->render(), which kind of negates the point of writing a class in the first place; the caller is still doing all the work and the class itself basically doesn't (can't) do anything.
I was too sleepy when I asked the question, so sorry for that, anyway to make things clear I prepared the question for 2 hours.
I'm trying to organize my code and decided to organize it mvc'ish(mvc-like), I don't know if I can follow all the principles, but I wanted to be at least close to that.
My application has a front-controller (dunno if my definition is right), so that all the http-request of my application will be passing through a single point, in my case the index.php in the root directory of my application.
Having said that I have set it up like that, you can imagine that I used .htaccess to direct all request to index.php.
I exploded the url and created an array out of it, $url[] like so. So whenever I access my app like this http://localhost/app/pagename it'll be accessing a controller (pagename_controller)
I did it like this :
$file = $controller_path . $page . '_controller.php';
if (file_exists($file)) {
require $file;
$class_name = ucfirst($page) . '_controller';
$target = new $class_name();
}
also I wrap it up in a Container, the 'decorator pattern', for future use, validations maybe.
like this :
$controller = new Wrap($target);
$controller->index();
I don't know if the use of $controller variable name is appropriate so please forgive me when it is all wrong.
I kinda think that I can setup my application like this :
user sends a request, how? by using the application means that he/she sends out a http-request, that will load the initial state of the application
As you can see in the diagram of my desired application structure, I was able to do only the first part which is to direct the request to a single entry (index.php)
Now the problems are the initialization of other parts of the application.
As of this moment, I have 3 files that I want to setup, but I am confused on how.
index_controller, index_view, Template
class Index_controller {
private $model;
private $view;
public function __construct(){
// optional model -> $this->model = 'index'
$this->view = 'index' //
}
public function index(){
$this->load->view($this->view)
}
}
class Index_view {
private $model;
private $template;
public function __construct(Model $model = null){
$this->template = new Template('default');
}
public function view() {
$this->template->assign('css', 'default_css'); // don't know if this is efficient
// or $this->template->assign('header', 'default_header');
// or $this->template->assign('sidebar', 'default_sidebar');
// or $this->template->assign('footer', 'default_footer');
// or any other things I want to use in the template
}
}
class Template {
public $data = array();
private $tmpl;
public function __construct($template) {
$this->tmpl = $template . '_tmpl.php';
}
public function assign($name, $value){
$this->data[$name] = $value;
}
// public function output
// function that will explode the data array and render it out as a webpage
// I'll create templates and
}
With that at hand, I want to know now how do I link those things together. At the moment I have a system folder that can contain classes, and I setup a autoloader for that folder.
I am thinking of creating a Controller class and View class that acts as the ActionFactory and ViewFactory as illustrated in the diagram, although I know that these are not their responsibilities.
I am thinking of this :
class Controller {
protected $load;
public function __construct() {
$this->load = new View();
}
}
class View {
public function __construct() {
// some things i don't know
}
public function view() {
// some things i don't know
}
}
What are your suggestions and comments in my setup. How can I initiate the triad?
Well, let's not get stuck on your implementation details too much. You can go read about securing your framework at some other time. Let's deal with your question...
I actually created a framework that works along the lines you are trying to implement. I think what you are missing is a RoutingHandler class. Routing is the physical manipulation of the URL, which tells your application which Controller to load, and which Action to run.
In my world I also have Modules, so the basic routing scheme is
Module -> Controller -> Action
These three items map to my URI scheme in that fashion. Variables can be appended also like so...
http://www.domain.com/module/controller/action/var1/val1/var2/val2
So, what happens after the URI is parsed, and control is passed over to the appropriate controller and action? Let's make some code up to demonstrate a simple example...
<?php
class indexController extends Controller {
protected function Initialize() {
$this->objHomeModel = new HomeModel;
$this->objHeader = new Header();
$this->objFooter = new Footer();
$this->objHeader
->SetPageId('home');
}
public function indexAction() {
$this->objHeader->SetPageTitle('This is my page title.');
}
}
?>
In the Initialize method, I'm setting some controller-wide stuff, and grabbing an instance of my Model to use later. The real meat is in the indexAction method. This is where you would set up stuff to use in your View. For example...
public function randomAction() {
$this->_CONTROL->Append($intSomeVar, 42);
}
_CONTROL is an array of values that I manipulate and pass onto the View. The Controller class knows how to find the right template for the View because it is named after the Action (and in a sibling directory).
The Controller parent class takes the name of the action method and parses it like so...
indexAction -> index.tpl.php
You can also do some other fun stuff here, for example...
Application::SetNoRender();
...would tell the Controller not to render inside a template, but just complete the method. This is useful for those situations where you don't actually want to output anything.
Lastly, all of the controllers, models, and views live inside their own directory like so...
my_module
controllers
indexController.class.php
someotherController.class.php
:
:
models
HomeModel.class.php
:
:
templates
index.tpl.php
someother.tpl.php
:
:
I could go on, but I'm writing this from memory, and there are some wrinkles here and there, but hopefully this gives you food for thought.
How can i call a controller::action from another modules controller::action in Zend.
dir tree
-modules
--auth
---controllers
--crm
--default
---controllers
how can i do something like this:
/* module\default\controller */
public function indexAction(){
$something = \model\auth\IndexController::doSomething();
}
UPDATE:
I know that something like this is possible in CodeIgniter via Modular Extensions
see here
$out = modules::run('module/controller/method', $param1, ....);
The only thing I know of that will approach that functionality is the ActionStack helper:
ZF Action stack helper
Controllers are not really intended to be called in the same manner as most other methods.
If you need to call an action from another, without go foward with the flow of your program, so there something wrong like RockyFord said.
Thinking in your explanation about a partial retrieving of information to buil a widget, I would create a helper in the library so it could be called from any place, something like:
-lib
--MyLib
---Controller
----Action
-----Helper
------ Foo.php
The Foo action helper could be:
class MyLib_Controller_Action_Helper_Foo extends Zend_Controller_Action_Helper_Abstract {
public setBar() {
// some code here
}
public getBar() {
// some code here for retrieving the partial
}
}
Then to call it from another controller action or even another lib function
// in a controller action
...
$foo = new MyLib_Controller_Action_Helper_Foo();
$foo->setBar();
$bar = $foo->getBar();
$this->view->bar = $bar;
...
Hope this helps
NamastĂȘ !!
If you want to do that, something is probably wrong with your design. Try to move the desired functionality to a third class for example to an action helper and call it from both controllers. That said, it should be possible to do
$a = new A_Controller();
$a->aAction();
or
A_Controller::aAction();
if aAction is declared static. (I have tried neither though.)
I agree with the RockyFord's suggestion that the action stack helper is probably your best bet.
Other solutions might include manually forwarding to another action ssomewhere else in your app with some paramaters.
function fooAction()
{
// Going to someplace else
$this->_forward($action, $controller, $module, $params);
}
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 php site which flows as shown below. Please note I'm leaving out most of the code (wherever theres an ellipses).
index.php
include template.php
...
$_template = new template;
$_template->load();
...
template.php
class pal_template {
...
public function load() {
...
include example.php;
...
}
example.php
...
global $_template;
$_tempalate->foo();
...
Now, this works fine. However, I end up having a ton of files that are displayed via the $_template->load() method, and within each of these files I'd like to be able to make use of other methods within the template class.
I can call global $_template in every file and then it all works fine, but if possible I'd really like for the object $_template to be available without having to remember to declare it as global.
Can this be done, and what is the best method for doing it?
My goal is to make these files that are loaded through the template class very simple and easy to use, as they may need to be tweaked by folks who basically know nothing about PHP and who would probably forget to put global $_template before trying to use any of the $_template methods. If $_template were already available in example.php my life would be a lot easier.
Thanks!
You can define globals before you include 'example.php'.
global $_template;
include 'example.php'
Or you could do this:
$_template = $this;
include 'example.php'
Or inside example.php:
$this->foo();
The use of global is strongly not recommended.
By the way, consider this :
-> index.php
$_template = new template;
$_template->load();
-> template.php
class template {
public function load() {
include 'example.php';
}
public function showMessage($file) {
echo "Message from '{$file}'";
}
}
-> example.php
<?php
$this->showMessage(__FILE__);
Will output something like
Message from '/path/to/example.php'
I recommend you to not use "global", as Yanick told you as well.
What you probably need is the Registry design pattern. Then you can add the template to the registry and give it to every object. In general I would recommend you to learn about design patterns. Here are some more you could learn.