Regarding the MVC pattern, the view layer is supposed to be the functions that 'return' HTML data, or the view layer is actually the HTML data itself?
View classes in MVC-inspired patterns for web (it is extremely hard to implement classical MVC on web, and impossible with only PHP) are responsible for presentation logic. Then create response to requests and juggle multiple templates.
The way how information gets from model layer to the chosen view largely depends on which of MVC-inspired patterns is implemented. If you are using MVP or MVVM, then information is provided by the controller and the view is passive ( but view is not a dumb template). If you are going with Model2 MVC or HMVC patterns, then view is active an requests information from model layer.
When this view has acquired the information , it decides with templates to combine. Or even if templates are even necessary. And then create the response.
The response that each View generated can be HTML, JSON, XML or just plain text. Or, if it's required, sent only a HTTP header (like when doing redirect). This all is part of presentation logic.
The borderline of model, controller and view can be shown this way:
/* model models/post.php */
<?php
class Post {
public static all() {
return Array(...);
}
}
?>
/* controller /posts/index.php */
<?php
require "../../models/post.php"
$posts = Post.all();
require "../../views/posts/index.php"
?>
/* view /views/posts/index.php */
<?php foreeach ($posts as $post): ?>
<p><?php echo $post['name']; ?></p>
<?php endforeach; ?>
Ideologically it may be just function but common practice is something like template in template engines. So view layer is html + data insertions (without logic of how we get and why). Again ideologically it is function too, but not function of programming language of framework, for example.
View is the "template"
Model is the "Data"
Controller is the "connector" and the algorithems places.
View Layer is subjected to what you want to be visible on client as a presentation. In most cases it is HTML, CSS and Javascript you can also use XML and JSON.
In genral,
Controller ask Model to provide Data.
Controller can than make changes to this data as it need.
Controller send data to View to present with help of template/html.
View Layer is the functions that return the HTML Data
Related
Using MVC I have something like this:
class Controller
{
//returns View
function indexAction()
{
$data = $this->getData($this->id);
$view = new ViewModel();
$view->setVariable('data' => $data);
//used to render HTML template + data above later on
return $view;
}
//gets data from DB
//currently also does business-proprietary computation on data
function getData($id)
{
//repository/dao pattern
$data = $this->repository->getData($id);
//Business Logic "derivation"
foreach ($data as $datum)
{
//that does not go into "Controller
//that does not go into "Repository"
//but where does it go? - that's my question
$derivedData[] = (new Business())->doLogic($datum);
}
return $derivedData;
}
}
Recap
I used Controller to get my data out of DB using Repository pattern, then placed received data into view. But business-related computations are left stranded.
Question
Where do I place my business logic computations that act on the data gotten from repository? The derived data which is to return to Controller later, to be placed into View?
My personal choices of architecture are usually to:
Have small controllers as thin as I can, doing only session and general right checking
Services that are handling all business logic, one (one classe yes) per potential feature I need
Services are querying repositories, and eventually manipulate the data in and out, but usually no Controller, nor view will do a ->save() anywhere.
This means that those services are usually designed to be independent from the database and easier to be tested because they only take care of one and only one task.
In your example, the whole function getData will be a service that I would call GetCarDataById. This assuming that you manipulate Cars, I don't like to leave data wandering alone.
EDIT: to make it clear, this kind of approach is not MVC to some definition, most people interpret MVC as putting all code either in controller, either in repositories (model). To others view, MVC doesn't mean that you have other classes, what I call services, and actually most of my code lives here.
The MVC pattern is clear for me.
From wikipedia:
The model directly manages the data, logic and rules of the
application. A view can be any output representation of information,
such as a chart or a diagram. Multiple views of the same information
are possible, such as a bar chart for management and a tabular view
for accountants. The third part, the controller, accepts input and
converts it to commands for the model or view.
Answering your question. The modifications goes in the model domain.
In a MVC pattern, what's the best way to handle when a single view could have multiple actions of the same type (e.g POST)?
Say for instance in a TODO list application. You might allow a user to create multiple lists. Each list could have multiple items. So a user navigates to site.com/list/1 which shows them all the items on the 1st list (1 is a GET parameter). There are then 2 forms (POST) on this page to allow a user to:
Create a new item
Delete an existing item
Should the bootstrap create a "listcontroller", inspect the POST variables and then call the appropriate method similar to :
$lc = new ListController();
if(strtolower($request->verb) === 'post'):
if(isset($_POST['title'])) :
$data = $lc->newItem($_POST);
$load->view('newitem.php', $data);
else if(isset($_POST['delete']) && isset($_POST['id'])):
$data = $lc->deleteItem($_POST);
$load-view('deleteitem.php', $data);
endif;// End if post title
else:
//GET request here so show view for single list
endif; //
Or is it better to just do something like
$lc = new ListController();
if(isset($_POST)):
//controller handles logic about what function to call
$data = $lc->PostAction($_POST);
// $data could also potentially hold correct view name based on post
$load->view();
else:
//again just show single list
endif;
I'm just struggling how best to have a controller potentially handle multiple different actions, as there's potentially quite a few nested if/else or case statements to handle different scenarios. I know these would have to sit somewhere, but where is cleanest?
I know that there are many frameworks out there, but I'm going through the whole "want to understand best practice" behind it phase. Or is this totally the wrong way to do it? Should the controllers actually be structured differently?
To begin with, I actually really like, how you are dealing with implementation of MVC. None of that rails-like parody, where view is managed inside the controller.
Here is what I think is the root of your problem: you are still using a "dumb view" approach.
View is not supposed to be a synonym for "template". Instead it should be a full object, which has knowledge-of and ability-to deal with multiple templates. Also, in most of MVC-inspired design patterns, the view instances are able to request information from model layer.
In your code the issue can be traced back to view's factory ( the $load->view() method ), which only gets what controller sends it. Instead controller should only change the name of the view, and maybe send something that would change the state of view.
The best solution for you would be to create full-blown view implementation. Such that view itself could request data from model layer and , based on data it received, decide which template(s) to use and whether to require additional information from model layer.
I think you're somewhat on the right track with the latter approach. However, you should not hard code the calling of actions in your bootstrap. The bootstrap should interpret the URL and call the action methods dynamically through the use of a function like call_user_func_array.
Also, I would suggest that you leave the rendering of views up to the action code so the action logic is self sufficient and flexible. That would allow the action to analyse the input for correctness and render errors or views appropriately. Also, you've got the method 'deleteItem' on your controller, but that should really be the work of a model. Perhaps you should read up some more on MVC and try to work with an existing framework to understand the concepts better before you try to implement your own framework (I would suggest the Yii framework for that).
Here's an example of how I think your logic should be implemented in a good MVC framework.
class ListController extends BaseController
{
public function CreateAction($title){
if(ctype_alnum($title))
{
$list = new List();
$list->Title = $title;
if($list->insert())
{
$this->render_view('list/create_successful');
}
else
{
$this->render_view('list/create_failed');
}
}
else
{
$this->render_view('list/invalid_title');
}
}
public function DeleteAction($id){
$list = List::model()->getById($id);
if($list == null)
{
$this->render_view('list/errors/list_not_found');
}
elseif($list->delete())
{
$this->render_view('list/delete_successful');
}
else
{
$this->render_view('list/delete_failed');
}
}
}
here is a great tutorial on how to write your own MVC framework
I am currently involved in the development of a larger webapplication written in PHP and based upon a MVC-framework sharing a wide range of similarities with the Zend Framework in terms of architecture.
When the user has logged in I have a place that is supposed to display the balance of the current users virtual points. This display needs to be on every page across every single controller.
Where do you put code for fetching sidewide modeldata, that isn't controller specific but needs to go in the sitewide layout on every pageview, independently of the current controller? How would the MVC or ZF-heads do this? And how about the rest of you?
I thought about loading the balance when the user logs in and storing it in the session, but as the balance is frequently altered this doesn't seem right - it needs to be checked and updated pretty much on every page load. I also thought about doing it by adding the fetching routine to every controller, but that didn't seem right either as it would result in code-duplication.
Well, you're right, having routines to every controller would be a code-duplication and wouldn't make your code reusable.
Unlike suggested in your question comments, I wouldn't go for a a base controller, since base controllers aren't a good practice (in most cases) and Zend Framework implements Action Helpers in order to to avoid them.
If your partial view is site-wide, why don't you just write your own custom View Helper and fetch the data in your model from your view helper? Then you could call this view helper directly from your layout. In my opinion, fetching data through a model from the view doesn't break the MVC design pattern at all, as long as you don't update/edit these data.
You can add your view helpers in /view/helpers/ or in your library (then you would have to register your view helper path too):
class Zend_View_Helper_Balance extends Zend_View_Helper_Abstract
{
public function balance()
{
$html = '';
if (Zend_Auth::getInstance()->hasIdentity()) {
// pull data from your model
$html .= ...;
}
return $html;
}
}
Note that you view helper could also call a partial view (render(), partial(), partialLoop()) if you need to format your code in a specific way.
This is a pretty simple example, but to me it's enough is your case. If you want to have more control on these data and be able to modify it (or not) depending on a particular view (or controller), then I recommend you to take a look on Placeholders. Zend has a really good example about them here on the online documentation.
More information about custom view helpers here.
When you perform such a task, consider using the Zend_Cache component too, so you won't have to query the database after each request but let's say, every minute (depending on your needs).
What you are looking for is Zend_Registry. This is the component you should use when you think you need some form of global variable. If you need this on EVERY page, then you are best adding it to your bootstrap, if you only need it in certain places add it in init method of relavent controllers.
application/Bootstrap.php
public _initUserBalance()
{
$userId = Zend_Auth::getInstance()->getIdentity()->userId;
$user = UserService::getUser($userId);
Zend_Registry::set('balance', $user->getBalance());
}
application/layouts/default.phtml
echo 'Balance = ' . Zend_Registry::get('balance');
That wee snippet should give you the right idea!
In this case, I usually go with a front controller plugin with a dispatchLoopShutdown() hook that performs the required data access and adds the data to the view/layout. The layout script then renders that data.
More details available on request.
[UPDATE]
Suppose you wanted to display inside your layout the last X news items from your db (or web service or an RSS feed), independent of which controller was requested.
Your front-controller plugin could look something like this in application/plugins/SidebarNews.php:
class My_Plugin_SidebarNews
{
public function dispatchLoopShutdown()
{
$front = Zend_Controller_Front::getInstance();
$view = $front->getParam('bootstrap')->getResource('view');
$view->sidebarNews = $this->getNewsItems();
}
protected function getNewsItems()
{
// Access your datasource (db, web service, RSS feed, etc)
// and return an iterable collection of news items
}
}
Make sure you register your plugin with the front controller, typically in application/configs/application.ini:
resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"
Then in your layout, just render as usual, perhaps in application/layouts/scripts/layout.phtml:
<?php if (isset($this->sidebarNews) && is_array($this->sidebarNews) && count($this->sidebarNews) > 0): ?>
<div id="sidebarNews">
<?php foreach ($this->sidebarNews as $newsItem): ?>
<div class="sidebarNewsItem">
<h3><?= $this->escape($newsItem['headline']) ?></h3>
<p><?= $this->escape($newsItem['blurb']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
See what I mean?
In our setup, we have models+mappers for all db objects. Then there are controller actions which prepare model objects for respective actions based on business logic.
We have send entire model object to the view and if view (html) wants to show first name, it can call $obj->getFirstName() or if some other view (pdf) can even call $obj->getFullName(). Is this how it is supposed to be done?
What if country was left empty and the view ignorantly calls $obj->getCountry()->getISO3Code() will be fatal since getCountry() returned false instead of a expected country object.
One option is to bother the view with IF.. etc so it is made safe. but does it not defeat the purpose that views should be dump without logic? or maybe I over stressed it.
should we send the entire model object to the view (as now) or safely prepare and send a array of viewable fields? It kinda it makes the action to be aware how PDF view looks like and html view looks like, again maybe defeating controllers purpose.
I confess that I struggle with the same question. When the controller/action sets values in the view - $this->view->someKey = 'someValue' - then there is an implicit expectation that that the controller is aware of what the view requires. I guess the general idea is that this is ok; the view is responsible for how to render the data it is passed.
There is nothing wrong with using if statements inside your view-scripts. It is pretty common to see something like:
<?php if ($someCondition): ?>
<!-- some markup here -->
<?php endif; ?>
in a view-script. For example, take a look at the partials associated to a pagination control.
I have created view-model objects - kind of a read-only version of my model intended for use in a view - that permits me to do things in a view-script that are a bit cleaner. For example, you could have a view-model object with a method like hasCountry(), so that your view-script could do something like:
<?php if ($viewmodel->hasCountry()): ?>
<p>Country: <?= $model->getCountry()->getISO3Code() ?></p>
<?php endif; ?>
Kind of a trivial example, but for more complex logic about the entity I am trying to render, I find that a view-model like this provides a home for some of that rendering-specific logic that doesn't feel right in the controller and seems a bit complex for a view-script.
I'm using the term "partial" to refer to a small section of presentational code which is repeated on many views. For example, a sidebar. In vanilla PHP, where the business and presentation logic is mixed, including a sidebar is no trouble:
if($someCondition) {
include('sidebar.php');
}
However, in an MVC design pattern, the presentational logic must be kept in the view whilst the business logic must be kept in the controller. If I wish to include a partial unconditionally, then this is unproblematic since I can just have include('sidebar.php') in my view. However, I can no longer do so conditionally because that if logic is banned from my view.
I have attempted a number of solutions but they all have problems. I am currently using Solution 2:
Solution 1
Create an include function in my view class which could conditionally include content from my controller. So in my controller I could have the following logic:
if($someCondition) {
$this->view->include('sidebar.php');
}
$this->view->show('index.php');
Problems: sidebar.php will need to be included into index.php at a specific point requiring the include method on the view object to do some sort of parsing.
Solution 2
Move control of the partials out of the view and put them into the controller:
if($someCondition) {
$this->view->show('header.php', 'sidebar.php', 'index.php', 'footer.php');
}
else {
$this->view->show('header.php', 'index.php', 'footer.php');
}
Problems: Moves a large portion of the presentational logic into the realm of the controller. It seems to be more natural to me for the view to decide whether or not to include the header. Indeed, every PHP MVC tutorial I can find, has partials under the control of the view and not the controller.
Solution 3
Duplicate the view and alter the clone so that it includes the sidebar. Then I could conditionally load one or the other in the controller:
if($someCondition) {
$this->view->show('indexWithSidebar.php');
}
else {
$this->view->show('index.php');
}
Problems: Duplication of code. Consider what would happen if I had 2 sidebars which I needed to be conditionally loaded. Then I would need index.php, indexWithSidebar1.php, indexWithSidebar2.php, indexWithSidebar1And2.php. This only gets worse with every condition. Remember that the entire point of taking the sidebar out as a partial was to avoid replicating it anyway and this approach seems to defeat the point.
Are any of these solutions the "right" solution and if so, how can I overcome their problems? Is there a better approach out there?
However, in an MVC design pattern, the
presentational logic must be kept in
the view whilst the business logic
must be kept in the controller.
IMHO: From an architecture standpoint, I push my business logic further back, out of the controller. We use services to handle all the business logic and repositories for data retrieval. The services call the repositories and then pass back our data model with all the business logic decided for us. Any logic outside that is really UI logic (show this, hide that), as our returned data could be (should be able to be) used in any kind of application, whether it's a mobile app, windows app, or web app.
You could use an extension helper method for your control, and in the model for the partial you can return EmptyResult() if you don't wish to render the sidebar. Or, more succintly:
<% Html.RenderAction<MyController>(x => x.Sidebar({params})); %>
And then in the controller:
public ViewResult Sidebar({params})
{
SidebarModel model = new SidebarModel();
//...get/build model
if ({someCondition})
{
return View("MySidebarPartialView", model);
}
return new EmptyResult();
}
Have your controller evaluate the condition and pass the result to your view. Then, the view can decide whether to include the partial.
For example, the controller can check whether a variable, $foo, isn't null. It passes the result of the comparison to the view via the model's property, $model->isFooed. In this case, the view can display the sidebar based on the value of $model->isFooed.