I just got into MVC architectures and I'm trying to create my own very simple MVC framework that I understand, with only the core functionality without the extras I don't need.
I made a View class, that can handle the rendering of all view templates.
My question is, should I make a View class for every view template instead of using one for multiple templates?
I did not see a reason why I would, but I thought I'd ask and be sure I'm not going against any MVC principles.
class View {
private $_vars = [];
public function __set($index, $value) {
$this->_vars[$index] = $value;
}
function render($fileView) {
$path = ABSPATH . '/view/' . $fileView . 'View.php';
if(!file_exists($path)) {
throw new Exception('View not found: '. $path);
}
foreach($this->_vars as $key => $value) {
$$key = $value;
}
require $path;
}
It kind of depends on your own wishes and requirements. I've seen many different view structures in MVC (like) frameworks. Your options include, but are not limited to:
Using one view class per page (class Login, class EditUser).
Wrapping even parts of the view in separate classes (class Textbox, class Form), with which you ensemble bigger views (class Login would consist of one class Form, consisting of two Textbox classes).
Using one view class to render multiple templates. Class User would then be responsible for rendering an AddUser template, and an EditUser template, etc.
It's all dependent on the size of the application and wishes in terms of performance / resources, programming-ease, readability ...
You should create only one class that handles templates in your framework. You can declare it as an abstract class and then, create multiple, more specific Views (like HTMLView, JSONView and etc that extends abstract class). But generally, your approach is correct. Based on MVC structure, View class should only locate the template file on the disk and render it. All loops and statements should be written within that template file.
In your case, the View class shouldn't be a singleton. For every template being rendered, you should create a new instance to avoid variables in private $_vars = []; getting mixed up within different controllers. The better approach would be to create static method within View class and then, invoke it like this: View::render($args) or View::render($args, $template_name).
You can do this in Symfony2-like style, so controller only returns array with variables to assign in the template (and optionally the path to .tpl, but it's good to define logic structure and force View to find it automatically). Please don't forget about returning correct response from your app, so think of headers, statuses etc.
I strongly suggest you implementing some templating engine to your app, like e.g. Twig. It will make your life easier and the view classes will be delivered out of the box with such an engine.
Related
I have a view - PageView - whose output is the concatenation of multiple ModuleView outputs. I'd wish to always initialize views (if necessary) and models in controllers. The reason for this is that I find it easy to initialize the given view based on controller action (many models are associated with multiple views, ie.: front-end and back-end views).
Am I to be beheaded if I initialize a controller in a view? If yes, what approach would you recommend to solve the issue of a view being the concatenation of other views?
PHP code
class PageView
{
public function Output()
{
# $this->modules was set by a method called from PageController
foreach ($this->modules as $module)
{
$module_controller = new ModuleController();
# Will initialize ModuleModel (ID of 23 in constructor) and ModuleView, then call ModuleView->Output(ModuleModel->GetData())
$module_controller->ShowFrontEnd(array('module_id' => 23));
}
}
}
The project is AJAX heavy, hence the need for page and module controllers. The goal is to offer easy extensibility and readibility by those who are experienced with the MVC pattern in PHP.
Yes, you will be beheaded. Controllers should be spawning/handling views, not the other way around. It is no longer MVC if you are initializing Controllers from a View.
I have been using joomla more than for a year now and familiar with the MVC pattern as well. But I'm unclear how Joomla uses MVC when showing a view. In the components deveopers use below two method.
1st method
class myView extends JView
{
function display($tpl = null)
{
//HTML & PHP goes here
}
}
2nd method
class myView extends JView
{
function display($tpl = null)
{
parent::display($tpl);
}
}
In the 2nd method they create a tmpl folder and place a default.php inside of it. All the HTML and PHP code then goes inside that file.
Now problem is why they use 2nd method when we can right away use 1st method? What's the industry standard? What are the pros and cons of above methods? What should be used and why and depending on what? Thanks
There is no "industry standard". But MVC has such concept as "separation of concerns".
The second approach is much closer to the spirit of MVC-inspired patterns. Views are supposed to be responsible for UI logic. And, if this logic require to generate response, views multiple templates to create it. Or it might choose just to sent an HTTP header.
The first approach is "quick'n'dirt" version. It often means, that UI logic has leaked in controller. Or, that developer does not understand why spaghetti code is a bad thing.
It looks like the second method is calling a parent so it's calling the base part of the template. Often times you have a base template and then individual content (pages) that uses that base template.
I know extending a class with the same name is not possible, but I was curious if anyone knew of a way to load a class then rename it, so i can later extend it with the original name. Hopefully like something below:
<?php
//function to load and rename Class1 to Class2: does something like this exist?
load_and_rename_class('Class1', 'Class2');
//now i can extend the renamed class and use the original name:
class Class1 extends Class2{
}
?>
EDIT:
Well, I understand that this would be terrible practice in a basic OOP environment where there are large libraries of class files. But i'm using the CakePHP MVC framework and it would make great sense to be able to extend plugin classes in this way since the framework follows a well established naming convention (Model names, view names, controller names, url routes (http://site.com/users), etc).
As of now, to extend a CakePHP plugin (eg: Users plugin) you have to extend all the model, view, and controller classes each with different names by adding a prefix (like AppUsers) then do some more coding to rename the variable names, then you have to code the renamed url routes, etc. etc. to ultimately get back to a 'Users' name convention.
Since the MVC framework code is well organized it would easily make sense in the code if something like the above is able to be implemented.
I'm trying to work out why this would be necessary. I can only think of the following example:
In a context that you have no control over, an object is initialised:
// A class you can't change
class ImmutableClass {
private function __construct() {
$this->myObject = new AnotherImmutableClass();
}
}
$immutable = new ImmutableClass();
// And now you want to call a custom, currently non existing method on myObject
// Because for some reason you need the context that this instance provides
$immutable->myObject->yourCustomMethod();
And so now you want to add methods to AnotherImmutableClass without editing either Immutable class.
This is absolutely impossible.
All you can do from that context is to wrap that object in a decorator, or run a helper function, passing the object.
// Helper function
doSomethingToMyObject($immutable->myObject);
// Or decorator method
$myDecoratedObject = new objectDecorator($immutable->myObject);
$myDecoratedObject->doSomethingToMyObject();
Sorry if I got the wrong end of the stick.
For more information on decorators see this question:
how to implement a decorator in PHP?.
I happen to understand why you would want to do this, and have come up with a way to accomplish what the end goal is. For everyone else, this is an example of what the author may be dealing with...
Through out a CakePHP application you may have references to helper classes (as an example > $this->Form->input();)
Then at some point you may want to add something to that input() function, but still use the Form class name, because it is through out your application. At the same time though you don't want to rewrite the entire Form class, and instead just update small pieces of it. So given that requirement, the way to accomplish it is this...
You do have to copy the existing class out of the Cake core, but you do NOT make any changes to it, and then when ever you upgrade cake you simply make an exact copy to this new directory. (For example copy lib/Cake/View/Helper/FormHelper.php to app/View/Helper/CakeFormHelper.php)
You can then add a new file called app/View/Helper/FormHelper.php and have that FormHelper extend CakeFormHelper, ie.
App::uses('CakeFormHelper', 'View/Helper');
FormHelper extends CakeFormHelper {
// over write the individual pieces of the class here
}
I've written a small MVC in PHP5 and desire a pagination module to be added to some files in my views section/folder..
I was wondering.. would the Pagination class be included in the Controller or Models section/folder?
Currently i've included it in the Models folder and called the function when needed..
The way I see it, pagination is a control, allowing user to tell your database (model), which portion of data he or she wants to see.
So I would go with the Controllers module.
Well, I think a better approach would be to make a helpers folder and then load them into your application like this :
function use_helper()
{
static $helpers = array();
foreach (func_get_args() as $helper)
{
if (in_array($helper, $helpers)) continue;
$helper_file = HELPER_PATH.DIRECTORY_SEPARATOR.$helper.'.php';
if (!file_exists($helper_file))
throw new Exception("Helper file '{$helper}' not found!");
include $helper_file;
$helpers[] = $helper;
}
}
Then all you have to do is build a pagination.php file with your Pagination class.
When you need it, you call the function
use_helper('pagination');
From here of course it depends on you Pagination class.
Hope this helps.
i guess the best approach is to call the pagination from the view, referring to this MVC
A view queries the model in order to generate an appropriate user interface
(for example the view lists the shopping cart's contents).
The view gets its own data from the model.
In some implementations, the controller may issue a general instruction to the view to render itself.
In others, the view is automatically notified by the model of changes in state (Observer) that require a screen update.
and because you will be using this class almost in every view, you should make a helper and include this class inside that helper so that all the views can share its methods
I have my own hand-rolled PHP MVC framework for some projects that I'm working on. When I first created the framework, it was in the context of building an admin CMS. Therefore, there was a very nice one-to-one relationship between model, view, and controller. You have a single row in the DB, which maps to a single model. The controller loads the model and passes it to the view to be rendered (such as into an edit form). Nice, clean, and easy.
However, now that I'm working on the front end of the site, things are getting sticky. A page isn't always a view of a single model. It might be a user directory listing with 20 users (each a User model). Furthermore, there might be metadata about the request, such as pagination (current page, total pages, number of results) and/or a search query.
My question is, what is the cleanest way to pass all this data to the view?
Some options I'm considering:
Have the controller create an array and pass that to the view as a single parameter:
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$data = array()
$data['models'] = $models;
$data['currentPage'] = $current;
$data['totalPages'] = $total;
return $view->render($data);
}
}
class UserView{
public function render($data){
// render the data
}
}
Create properties in the view class and have the controller populate them:
class UserView{
public $models;
public $currentPage;
public $totalPages;
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->models = $models;
$view->currentPage = $current;
$view->totalPages = $total;
return $view->render();
}
}
Give the view some sort of generic HashMap or Collection object as a container which can hold any arbitrary number and name of data.
class UserView{
public $collection = new Collection(); // works like a Java collection
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->collection->add($models,'models');
$view->collection->add($currentPage,'currentPage');
return $view->render();
}
}
I know that technically any of the could work, but I'm unsure of the best choice, or if there's a better or more conventional choice that I'm missing.
I'm going to recommend the concept of Fat Models, Skinny Controllers (or, Fat Models Thin Controllers if you prefer...)
In otherwords, your model is too strict - tying your model to represent only something like a RowDataGateway is extremely limiting.
In fact, I think good models hide the fact that you're reading the data from a database at all. Because, in reality, your data could be in text files, or from a web service, or whatever. If you treat your Model like nothing more than a glorified DBAL, you doom yourself to having tightly-coupled code in your controllers that just won't let you break away from the "data only comes from the database" way of thinking.
I've seen both of the first two methods implemented in popular MVC/templating frameworks.
django uses the first method, passing to the view a dictionary of variables which the view uses to fill the template.
smarty uses the second method, creating a Smarty object and assigning values to each the properties in the container.
Your third method seems to essentially be the same as the second, with minor architecture differences.
Really, I guess I haven't said anything that you haven't thought of already. Basically, these are all sounds ideas, so implement whatever you feel you are most comfortable with.
In the one I use, it has automatically has a view property in the controller that you can access methods and properties on the view. All public properties are then accessible within the view view '$this' since the view is rendered in it's own objects context.
In the controller:
$this->view->myVar = 'test';
And in the view:
$this->myVar; // 'test'
The same goes for the layout since the are both separate instances of the same view object:
$this->layout->myVar = 'test';
And then in the layout:
$this->myVar; // 'test'
The framework used to be proprietary, but is about to be released to the general public. I'd be happy to send you some code from it if you think that'd help. Remember, the simplest answer is usually the best answer.