tags:

views:

198

answers:

4

Rather than use a full-blown PHP MVC, I'm designing one that will best-fit my uses. I have the basic framework done, and have coded the models and controllers I'll need to run my website.

Now I'm moving onto the Views, and I've encountered a small dilemma. My approach is working fine for me, but for future reference, I want to know if what I'm doing is a bad habit to get into.

What I'm trying to do:

In my View, I'm calling a Model that runs my authentication system, and requesting the login status of a user. I then use that boolean to decide whether to show certain elements within the view, and where to place others.

Should I be designing separate views for each login status, or is this approach fine? However, if I'm going to be implementing this MVC into the work I'm doing for my clients, I need to use the best practices.

Any advice would be appreciated!

A: 

While I only know enough about MVC to get myself in trouble, I've always lived by the fact that unless your view is STRICTLY user interface, then it shouldn't be there.

Also, I've also ran with the idea of thin controllers, fat models.

Going off of these, I'd suggest adding a method to your authentication system model that returns the appropriate view to render and passes that to the view.

WedTM
But I thought one of the main reasons for using an MVC was to maintain that layer of abstraction between the models and views so that the data-accessing code isn't also responsible for rendering a view.
BraedenP
You'd usually use a controller to mediate between the 2
K Prime
that layer of abstraction is indeed one of the main reasons, and it's called controller. Basically, think of the controller as the entry point thats being called by the user. It just collects data from the models ( or sends data there ) and renders something.
moritz
This is correct, however, you're not displaying anything from the model, your simply deciding which view needs to be displayed.If you were to write to the output buffer from your model, then that would be breaking the abstraction.
WedTM
You're not even deciding whats displayed in the model. The model actually doesn't know anything about the controller or any views. Theoretically :-)
moritz
@moritz is correct in the end, it should be done in the controller.
WedTM
A: 

Okay, I would really try to keep my Views as logic-free as possible. If you need to switch anything based on e.g. the result of a model method, put a controller in place that delegates the rendering.

Just make sure you follow the basic idea: Do your stuff in the models, Tell your models what to do from your controllers and also tell your views what to show from your controllers.

moritz
That's what I was thinking as well, but I thought that perhaps something as simple as a boolean check in the view would be forgivable. However, if the rule is *not* to do that, then I'll just do the check in the controller and pass the appropriate variable to the view. Thanks.
BraedenP
Why not simply get the value from the model, i.e. $allowed = superUserCheck();and then later if ( $allowed ) render 'foo' else render 'bar'that way, MVC is maintained and you have clean views and clean controllers. AND! clean models.. ( edit: naa no markdown in comments.. )
moritz
+6  A: 

You can, but you shouldn't. Except for a few extreme cases (and branching your view based on logged-in status is definitely not an "extreme case"), it's pretty much always A Bad Idea to call model stuff from a view.

What you probably want to do in your situation is pass the boolean to the view through the controller. That way, if you change something about the User model, the view doesn't have to know, so long as the controller keeps the behavior the same.

jboxer
Well what I was doing was splitting my views into "elements" so I could more easily and efficiently reuse code. I built into the MVC a method of loading those "elements" into other "parent" views. The boolean check was in an element, and I wanted to make that element standalone so that it'd work in any view without requiring me to set a variable in the parent view. However, it'll simply require one extra line of code per parent view, so it's not that much of a pain to do it the right way.
BraedenP
One other suggestion: check out Django's context processors: http://docs.djangoproject.com/en/dev/ref/templates/api/ They allow you to load up data that all templates may want, rather than explicitly pass the relevant data to each view separately. Some bare-bones version of context processors may get you what you're looking for.
jboxer
+1  A: 

Can I call the model from the View?

Yes, you can. As long as you maintain the separation of concerns between M,V and C, you are free to call upon the Model (or the Controller) from the View. Most MVC diagrams show a bidirectional connection at least between View and Model. What you don't want to do though, is place logic/code from the Model (or the controller) into the View and you don't want to modify the model from there.

For example, you might have a widget on your page that aggregates the latest ten blog posts headlines from your favorite blogs on each page of your website. You get the headlines by calling, say MyFavFeeds::getLatest(); in your model. What are your options now?

  1. You could add the code to fetch the headlines into the controller, but that would require you to replicate it in each and every controller action, which is against the DRY principle. Also, the controller's concern is handling user input for specific actions and fetching the headlines on each call is likely not even related to these actions.
  2. If your architecture supports it, you could fetch that data in some sort of preDispatch hook, that is, the headlines get loaded and injected into the View from a plugin or callback. That would be DRY, but a second developer might not be aware of that plugin and accidently overwrite the variable holding the headlines from his controller action. And there might be cases in which you wouldn't want to load the headlines, e.g. when just rendering confirmation pages for form submissions, so you'd have to have mechanism for disabling the plugin then. That's a lot to consider.
  3. You place the call to (not the code of) MyFavFeeds::getLatest() into the View or Layout template or, better, a ViewHelper, that encapsulates the call to your model class and renders the widget. This way you don't have to worry about overwriting any variables or repetition. And when you don't need the headlines on your view, you simply don't include it.

About your other question:

In my View, I'm calling a Model that runs my authentication system, and requesting the login status of a user. I then use that boolean to decide whether to show certain elements within the view, and where to place others.

Authentication is something you will want to do early in the application flow, before any controller actions are called. Thus, you should not run your (entire) authentication system in the View. The actual authentication is not View-related logic. Just requesting the user status after authentication, on the other hand, is okay. For instance, if you want to render a widget showing the user name and giving a login/logout button, it would be fine to do something like

<?php //UserHelper
class UserMenuHelper
{
    public function getUserMenu()
    {
        $link = '<a href="/user/logout">Logout</a>';
        if(MyAuth::userHasIdentity()) {
           $link = sprintf('<a href="/user/logout">Logout %s</a>',
                            MyAuth::getUsername());
        }
        return $link;
    }
}

If you got larger portions of your GUI to be modified by a User's role, you might want to break your View apart into partial blocks and include them based on the status, instead of writing all the HTML into a View Helper.

If you are only looking to render a navigation based on the user role, have a look at Zend Framework's Zend_Navigation and Zend_Acl to see how they do it.

Gordon
-1: Regarding your "favorite blogs on each page as a widget" example, I'd rather use View Composition than calling a Model from a View. Model/View-separation should weigh a lot more than your DRY-argumentation here IMO.
Karsten
@Karsten the above approach does not violate M/V separation. The logic to get the headlines is still capsuled inside the Model. You just bypass the controller, which is perfectly acceptable in the example, given that there is no user input to handle. A Composite View is just a different approach to the same problem, but in the end you still just got a View and that's where it runs in circles, because then it depends on how and where you do the assembly. Feel free to provide an answer to the OP showing him a Composite View approach that's not fetching the headlines from the View.
Gordon
Ah, thanks. That cleared things up a lot! And as for my Auth system, I'm not authenticating in the view; I'm simply requesting the login state from the Model within the view.
BraedenP