I'm very new to MVC, and so I've been scouring the net in an attempt to build my own framework to get a real understanding on how the whole concept works.
Anyway, almost all tutorials out there that deal with MVC always seem to assign data that needs to be displayed in the view to an intermediary variable that is THEN used in the view.
My question is, why bother with that extra step?
Most MVC implementations end up including the view WITHIN the controller... so if that's the case, why waste time/memory/cpu cycles to create an intermediary variable/array that is then passed to the View when the View ends up being included with the controller at the end.
Would it not make more sense to simply use the Controller variables directly in the View itself?
Here's a code example to hopefully clarify what I mean:
class News_Controller
{
public function main(array $getVars)
{
$newsModel = new News_Model;
//get an article
$article = $newsModel->get_article($getVars['article']);
//create a new view and pass it our template name
$view = new View_Model($this->templateName);
//assign article data to view
$view->assign('title' , $article['title']);
$view->assign('content' , $article['content']);
$view->render();
}
The render function is basically just an include to bring the View into the Controller to be displayed down the chain. If that's what's going on, one could simply use $article directly in the View rather than go through the hassle of assigning variables to the View.
Keep in mind that PHP copies on write. So there is no major performance hit to a simple variable assignment.
As already mentioned, scope is a big issue here. The view is a separate entity from the controller and doesn't have access to its data. Of course, you could pass an instance of the controller to the view, but that's creating an unnecessarily too strict of coupling between the two. The view should be able to work independent of the controller.
So by explicitly assigning data to the view you decouple the two. You will tend to write better and cleaner code as a result.
Second, the process of assigning data to a view could do some data sanitizing or other extra work. For instance, in my framework, I consider all data passed to an HTML view as unsafe. When data is passed to the view (unless explicitly marked as safe) it is encoded via htmlspecialchars.
Finally, you can always assign objects or arrays to the view:
$view->assign('article', $article);
If you do this you generally don't need to assign very much stuff. (And if you do, perhaps your page is doing too many different things.)
MVC is a very loose categorization. You are describing one way it could work. It's also possible that the variables you use in your controller may not be intended to be used as-is within your view. You may have some sort of template processor that takes in data from the controller, alongside a specially marked-up view template, and spits out the result. Or you may be calling functions/methods from within your view that return ready-made markup.
your include inherits everthing from the render() method's variable scope, but the render() method does not inherit anything from the controller's variable scope.
class foo {
public function bar() {
echo $somevar;
}
}
$somevar = 'test';
$foo = new foo();
$foo->bar();
this code will echo nothing and give you a warning that $somevar has not been defined (if your error reporting is set to show warnings). the reason for this is because methods and functions do not inherit the scope of where they were called from.
php.net/manual/en/language.variables.scope.php
php.net/manual/en/language.oop5.visibility.php
Because of the scope the controllers variables are in. Unless you make everything global (really bad idea) your concept won't work.
Related
I'm building a simple MVC Framework and stuck in how i set up the objects.
TL;DR: Is better initialize objects before the view or during the view render?
Example:
CONTROLLER
<?php
class Controller {
public function __construct() {
$user = new User();
}
}
?>
OBJECT USER
<?php
class User {
public function __construct() {
$this->setFriends($arg);
}
public function setFriends($arg) {}
public function getFriends() {}
}
?>
OBJECT FRIEND
<?php
class Friend {
.. properties ..
.. methods() ..
}
?>
VIEW
<?php
foreach($user->getFriends() as $friend){
.. $friend is a Friend Object already ..
.. html...
}
?>
The question: Is better initialize Friend object on setFriends method (before the load view - remember, there are a lot of friends) or on getFriends method (on load view)?
public function setFriends($arg) {
foreach($arg as $item)
$this->friends[] = new Friend($item)
}
OR
public function getFriends() {
$tmp = array();
foreach($this->friends as &friend)
$tmp[] = new Friend($friend)
return $tmp;
}
I think in the first case, memory will be pre consumed. And the second case, the Friend Object will initialize only if the view call getFriends.
As a general rule of thumb:
Anything that's required to make every page load should be required universally early on in the initialization (usually in a boostrap layer or similar). This includes things like a base controller (from which others extend), base view (same deal here), database handler object, etc.
Anything that's specific to just one page, as you seem to be describing with users and friends, should be loaded in the controller or controller action which handles loading that page. This helps keep your code focused and your memory footprint down.
In these cases it is always better to move as much of the business logic out of your views as you can, and save your PHP in views for simple things like loops and echos. In MVC frameworks you'll often see arrays built in a controller, so that lots of data that has already been finalized can be passed to the view. For example, within your controller you could instantiate your user to pass to the view as an argument, and then also instantiate your friends and add them all to an array of friends that you pass as another argument. Or combine these two arrays into one big array, and pass a single 'parameters' argument to the view. This would then be a standard parameter that all your views could share, and then picking apart the array of data happens within the view itself.
Other options become more viable depending on what information you need to be available about friends. For instance, when instantiating a user you could also (within the constructor) instantiate each one of their friends, assign to an array, and save them all as a property of that user. This does make a bulkier object, and you have to consider if you're using a lot of users how much this will cost.
You could, however, only need friends in certain circumstances so it may make more sense to instantiate them when you need them, instead of always having them even when you don't. If this is the case, your user should at least have a lookup of its friends, and be able to set a property within itself that will hold the info you need to look up a friend. This means that whether it's included in your user constructor (if you'll always need to know what friends a user has), or in a separate function like getFriends (if you only sometimes have to know about a user's friends), you'll need to have at least an ID of each friend that can be stored as a property of your user, so you can later loop through it and instantiate friends based on id.
Overall I think the important point is regarding context. Where you create an object directly affects two main things: where it is accessible, and how much memory you waste. You always need to weigh those two and strike the balance. My best advice is to restrict where data exists and where it is accessible as much as possible, to only those places where it has to be. This will keep you application the most secure and use the least memeory.
I know it's a lot to think about, but I hope this helpled!
I have a class called Page, which loads a PHP file for the current page, containing all the HTML (template file). In my template file, I want to use instances of other objects, that is initialized outside the Page class (ex. User, PDO or other classes). My problem is, how I do this the smartest way.
In my page class i have a method called get_page() which loads my template file (containing all the code for my GUI.
public function get_page() {
// Load theme template
...
$template_file = ABSPATH_THEME . 'tpl.' . $result['template_file'] . '.php';
if(file_exists($template_file)) {
$page = require_once($template_file);
return $page;
}
}
As you see the template file are loaded inside my Page class, and therefore will it not access instances of classes initialized outside my Page class.
I can come up with different solutions:
1) I pass all instances of the different classes to my class Page, when constructing my page. I think this is the right way, but can be very complex if I need 5, 10 or 20 different objects in my design.
2) Find a way to include the template file outside the Page class, but triggered from the get_page() function - have no clue how to do this, and if it is a good solution?
Can you please tell me what is best, and if there are some better ways to do it?
You can just include classes on the top of your php
eg. /CLASSPATH/ClassName.php
And then you can create an entity for that class once and use it everywhere
eg. $entity = new ClassName();
I think your best bet will be to pass them in as arguments, depending on what you need you may want to make them properties.
Edit: This is assuming that by "using instances of other objects" you mean that you need to use objects that have already been instantiated elsewhere.
If by "istances of other classes" you mean other generical objects (you mention PDO and user which are goos examples of this) I would store these in the $_SESSION array (or in $_GLOBAL array, $_SESSION being the best option in most cases).
Then you can access them just by using $_SESSION['PDO']->... and the like
One of the most popular method to handle php application rooting is to implement MVC design patern, or use an MVC Framework. Google 'MVC php' for details, good luck.
Take a look at the functions get_defined_vars and extract. Using these you can export variables from one scope to another.
A silly example could be: A function A transfer locally defined variable to a function B.
function A()
{
$var1 = "1";
$var2 = "2";
// etc
$data = get_defined_vars();
B($data);
}
function B($data)
{
extract($data);
// somescript.php can use $var1, $var2, etc if B is call from A.
require("somescript.php");
}
A();
The best practice is to use a ServiceLocator or an InversionOfControll-Container to retrieve class instances without violation of DI-Principle. For your template file you can create view helper objects to have direct access to other objects.
In relation to the question Passing default variables to view, to pass variables available among all views, is there a technical or functional difference between the use of View::composer():
View::composer('*', function($view) {
$thundercats = 'Woooooohh!!';
$view->with('thundercats', $thundercats);
})
in the filters.php file or the use of View::share() in the BaseController.php file:
public function __construct {
$thundercats = 'Woooooohh!!';
View::share('thundercats', $thundercats);
}
I've only recently learned about View::share() and find it exceptionally intruiging although I've already started using the former in another project.
Edit:
My first assumption is that the former is a file (filters.php) while the the latter is a class (BaseController.php). With this in mind, I'm guessing a class is much better? Although, I'm not quite sure why at this point. :)
Technically they are not at all alike. View::share simply sets a variable, while View::composer is a callback function.
Let me explain in greater detail:
View::share is really straight forward it sets a variable which can be used within any of the views, think of it like a global variable.
View::composer registers an event which is called when the view is rendered, don't confuse it with a View::creator which is fired when a view is instantiated.
View::composer / View::creator can both be used as a class which is well documented.
While these give you the ability to pass additional data to a view, they also give you to ability to do a lot of other things, for example they could:
Aid in debugging a view
Log information about views
Be used to create custom caching (probably not a great idea, but possible)
These are just some examples of what could be possible using View::composer and View::creator.
View::composer('*', callback());
Means that the callback will be called for all views (*).
View::share
Means that a variable will be shared with all outputed views.
Because the first is in filters.php, it'll apply for all routes.
The second is in a controller contructor, so it'll apply for all views triggered by this controller.
One last thing: when overriding a constructor, it's a good pratice to allways call the parent constructor with this code:
parent::_construct();
I am passing a variable to a view script from a controller with Zend Framework. I want to know if there is anyway I can pre-filter the view so that I can change how the variable is retrieved in the view script.
For example. In the controller I have:
$this->view->name = 'Bob';
And in the view I have:
echo $this->name;
Which works fine no question! What I want is to occationally have the ability to change it so I can just use:
echo $name;
So basically, removing the $this statement. Is this possible? I am making a template and have other designers using the template system and want to make it easier for them then to type $this->array->name all the time.
I know in the view script I can simply add:
$name = $this->name;
But I would like to do that in the controller somewhere.
Thanks for your advice!
I'll say right up front that I think this is not a good idea to monkey around with the scope of your view's output variables. This works against the framework's MVC pattern by exposing the view's internals all over the global scope. But if you really insist on doing this, in the controller, you can probably assign them globally like this:
$GLOBALS['name'] = $this->view->name;
And access them later as $name or $GLOBALS['name'].
Note: depending on when the view's variables get populated versus when you assign them in the controller, you might need to assign references.
$GLOBALS['name'] = &$this->view->name;
Your idea is reasonable and seems quite easy to achieve. Just add this line to the beginning of your template:
<?php extract(get_object_vars($this)) ?>
Passing state to view by the means of global variables might not be considered a good idea by some, but this way is safe, as extract creates local variables, not global ones.
If you would like to reuse this method and prefer not to copy&paste, you could easily inherit from Zend_View, introduce this line to method (if I recall correctly) _run, and then in your bootstrap:
create an instance of the controller,
get the ViewRenderer action helper (using static method of Zend_Controller_Action_HelperBroker)
inject your view instance into ViewRenderer
This way all your views will present their public properties as variables.
Can someone please tell me what the best way to pass values from a controller into a view would be? If anyone has played with codeignitor, they will know what I mean. I have looked at CIs code but can't find the file that handles this. I'd LOVE to know how this is done.
Thanks!
There's not necessarily a "best" way as far as I know, but there is a common method that I've seen used many times, and have used myself. It generally involves an associative array, and either the extract() function or variable variables.
Basically, all you do is set up your data into an associative array, using keys that will become your template variables.
//inside the controller
$data['name'] = 'my name';
$data['zip'] = '90210';
The $data array gets passed to the view somehow, either directly or indirectly, and extracted via extract() or using a loop of variable variables (same thing, really). The template can then be included, and the variables are in local scope.
//inside the view rendering process
extract($data);
//$name and $zip now exist
Code Igniter follows this exact procedure. Inside system\libraries\Loader.php in the most recent version (1.7.1) there's a function called view(), which is what you call in your CI controller to load a view/template (same thing really in CI). You pass a data array as the second parameter.
view() calls an internal function called _ci_load() in the same file, which extracts the data you passed it (and does some other wacky caching stuff). Your variables are ready to go after that in the local function scope, and can be manipulated inside the template after the subsequent include(), since everything happening in the included file exists in the local _ci_load() function scope as well.
I've used the exact same design in a quick-and-dirty homebrew MVC set up before. It's quite effective.
You might want to try CakePHP's 15-min blog sample. I haven't tried Code Igniter.
In Zend Framework, it's as simple as
class IndexController {
public function IndexAction {
$this->view->name='Name';
}
}
with the $this->view->xxxx setting the variable in the view.