Proper way to send data to MVC Views in Zend - php

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.

Related

Should loops with logic and html output go in the model or the view?

I'm new to OOP and MVC and currently building a website using CodeIgniter.
There seems to be a lot of contrasting information about whether loops should be in the view or the model.
On the one hand I'm trying to keep all my html markup inside the views, but on the other hand I want to keep my messy PHP logic outside of the view. Plus I also need to format the data inside my loops using functions located in my model.
What's the best way to go about organising this?
Here is a simplified version of my current implementation:
View
<section>
<ul>
<?php echo $albumTracklistHtml ?>
</ul>
</section>
Controller
$data = [
'$albumTracklistHtml' => $this->MyModel->getAlbumTracklistHtml()
];
$this->load->view('myPage', $data);
Model
public function getAlbumTracklistHtml()
{
//$this->tracklisting returned from db call in other function
foreach($this->tracklisting as $song) {
$mp3 = $this->convertToAmazonUrl($song['mp3']);
$art = $this->formatArtUrl($song['art']);
$name = $this->formatTrackName($song['name']);
$class = 'mp3';
$btn ='';
if(substr($name, 0, 1) == '*') {
$class = 'load mp3';
$btn = '<span class="playBtn"></span>';
}
<li class="'.$class.'" '.$mp3.'>'.$btn.$name.'</li>';
}
}
Very generally speaking, and keep in mind that this isn't a hard and fast rule and if you ask ten different people you'll get ten slightly different answers, but the job of the model view and controller are essentially:
The model provides a way for the controller & view to access data from another source (a database, for instance). It's basically an abstraction on whatever your data is stored in.
The views simply display data they are given.
Controllers connect the model's data with the view, so it can display the data.
I would argue that the example code you've posted is just fine, and fits these definitions. Your model retrieves the data (or processes it), the controller hands the resulting data to the view, and the view simply displays it.
However, I also think it's fine (and generally I prefer this) for the model to simply return a list of items, and then for the view to loop through them and display each one. Of course, the view "shouldn't" be doing a lot of processing, but outputting HTML for each item seems like exactly what it should be doing. The reason I prefer this is purely for separating concerns - your models should be fairly HTML-agnostic. As in, if you ever wrote a non-web-based application to interact with the same data, it could use the same models. Because of this, I would put any HTML-rendering code in my views. Even though it requires some looping logic.
At the end of the day, though, I don't think it matters that much in your case. If you strongly prefer putting the loop in the model, go with that. The most important thing is just to develop your own conventions, and then stick to them.
Here's how I would do the view:
<section>
<ul>
<?php foreach($album->getTracks() as $track): ?>
<li
class="<?php echo $track->isPlayable() ? 'load mp3' : '' ?>"
>
<span class="playBtn">
<?php echo $track->getName() ?>
</span>
</li>
<?php endforeach ?>
</ul>
</section>
This assumes that you've passed a variable called $album, and that a method offered therein returns an array of type Track.
You can return arrays if you like as well, however I prefer objects as you can convert complex conditions to simple, meaningful names. Thus, rather than your '*' test, the programmer calls $track->isPlayable(), which makes much more sense, and doesn't need commenting in the template.
In my experience it depends on what the loop is doing. One can have view specific loops. You can loop through html tags that display elements dynamically and that should go in the view.
for($controller_sent_array as $element)
{
echo "<h4>$element</h4>";
}
However things like looping through file input should go in the controller/model side. I make a point of being vague here because the choice of what should be in models verses controllers depends on framework optimizations. However what is important is that the model and controller are not sending html to be rendered. Rather, they should process data either from a database or user input or network connection, and package that data for the view to figure out. To this end you also need loops.
// This is not a safe way to do this in real life...
for($_POST as $post_input)
{
$this->your_database_library->save_input($post_input);
}
Consider your framework selection with respect to what loops are processing what where, but loops should be used when needed in the model, view, or controller.

PHP: Regarding the view layer in MVC?

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

Correct way to deal with application-wide data needed on every pageview

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?

How should partials be loaded when they are dependent on business logic?

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.

CakePHP View including other views

I have a CakePHP application that in some moment will show a view with product media (pictures or videos) I want to know if, there is someway to include another view that threats the video or threats the pictures, depending on a flag. I want to use those "small views" to several other purposes, so It should be "like" a cake component, for reutilization.
What you guys suggest to use to be in Cake conventions (and not using a raw include('') command)
In the interest of having the information here in case someone stumbles upon this, it is important to note that the solution varies depending on the CakePHP version.
For CakePHP 1.1
$this->renderElement('display', array('flag' => 'value'));
in your view, and then in /app/views/elements/ you can make a file called display.thtml, where $flag will have the value of whatever you pass to it.
For CakePHP 1.2
$this->element('display', array('flag' => 'value'));
in your view, and then in /app/views/elements/ you can make a file called display.ctp, where $flag will have the value of whatever you pass to it.
In both versions the element will have access to all the data the view has access to + any values you pass to it. Furthermore, as someone pointed out, requestAction() is also an option, but it can take a heavy toll in performance if done without using cache, since it has to go through all the steps a normal action would.
In your controller (in this example the posts controller).
function something() {
return $this->Post->find('all');
}
In your elements directory (app/views/element) create a file called posts.ctp.
In posts.ctp:
$posts = $this->requestAction('posts/something');
foreach($posts as $post):
echo $post['Post']['title'];
endforeach;
Then in your view:
<?php echo $this->element('posts'); ?>
This is mostly taken from the CakePHP book here:
Creating Reusable Elements with requestAction
I do believe that using requestAction is quite expensive, so you will want to look into caching.
Simply use:
<?php include('/<other_view>.ctp'); ?>
in the .ctp your action ends up in.
For example, build an archived function
function archived() {
// do some stuff
// you can even hook the index() function
$myscope = array("archived = 1");
$this->index($myscope);
// coming back, so the archived view will be launched
$this->set("is_archived", true); // e.g. use this in your index.ctp for customization
}
Possibly adjust your index action:
function index($scope = array()) {
// ...
$this->set(items, $this->paginate($scope));
}
Your archive.ctp will be:
<?php include('/index.ctp'); ?>
Ideal reuse of code of controller actions and views.
For CakePHP 2.x
New for Cake 2.x is the abilty to extend a given view. So while elements are great for having little bits of reusable code, extending a view allows you to reuse whole views.
See the manual for more/better information
http://book.cakephp.org/2.0/en/views.html#extending-views
Elements work if you want them to have access to the same data that the calling view has access to.
If you want your embedded view to have access to its own set of data, you might want to use something like requestAction(). This allows you to embed a full-fledged view that would otherwise be stand-alone.
I want to use those "small views" to
several other purposes, so It should
be "like" a cake component, for
reutilization.
This is done with "Helpers", as described here. But I'm not sure that's really what you want. The "Elements" suggestion seems correct too. It heavily depends of what you're trying to accomplish. My two cents...
In CakePHP 3.x you can simple use:
$this->render('view')
This will render the view from the same directory as parent view.

Categories