Asked  7 Months ago    Answers:  5   Viewed   37 times

I have to seem problems grasping the concept of Views in MVC, they are, according to what I've read, the layer that manages the presentation in the aplication, but many of the material I've been reading seem to be different regarding this matter in this one from PHP Master.com.

The View is a class with functions that return some HTML code, where is the rest of my HTML? should it be put in independent .html pages that access this View code?

In this article, from php-html.net the View is a simple HTML file with a .php extension, but how are they accessing that data? I see no require() or anything like the instantiations in the first tutorial.

 Answers

34

Note: the MVC and MVC-inspired patterns are advanced constructs. They are meant to be used in codebases where ordinary object-oriented (that follows SOLID and other guidelines) code starts to become unmanageable. By introducing this pattern you would impose additional constraints, which then lets you to contain very complex applications. MVC is not meant for "hello world" apps.


Let's start from the beginning ...

The core idea behind MVC and MVC-inspired design patterns is Separation of Concerns. Said separation is two-fold:

  • model layer is separate from UI layer:
  • views are separated from controllers

enter image description here

Model layer (not "class" or "object") would contain several groups of structures, each dealing with as different aspect of business logic. The major parts would be:

  • domain objects: validation, business rules
  • storage abstraction: persistence and caching of data from domain objects
  • services: application logic

Also there might be mixed in repositories, units of work and others.

UI layer mostly consists of views and controllers. But they both utilize services to interact with the model layer. Services provide the way for controllers to change the state of model layer and for the views to gather information based on that new state.

In context of web the views and controllers form a loose pair, because of the request-response nature that web applications exhibit.

It should be noted that although controllers can alter the state of the current view directly, it's more common that these changes are effected through the model. One reason to alter the view directly is, for example, when instead of XML you need to respond with JSON.

Though it also could be argued that one could simple instantiate a different view for each output format and take advantage of polymorphism.


What is not view?

There is a widespread misconception that views are simply glorified template file. This mistake became extremely popular after release of RubyOnRails prototyping framework.

Views are not templates. If you use them as such, you break the core principle behind MVC and MVC-inspired patterns.

If you pretend that templates are views, it has an enormous impact on your architecture. There is no place for presentation logic in the view, therefore you push the presentation logic either in controller or model layer. The usual choice is "controller", because most of people understand that presentation logic has no place in model layer.

Essentially, this causes a merger of views and controllers.


What is view doing?

The responsibility of the view is to deal with presentation logic. In context of web the goal for view is to produce a response to the user (which, btw, is the browser not the human).

Technically it would be possible to create client side views, that user web sockets to observe model layer, but in practice it's virtually impossible to implement. Especially not in PHP environment.

To create this response view acquires information from model layer and, based on gathered data, either assembles response by distributing data to templates and rendering or sometimes simple sending a HTTP location header.

When using Post/Redirect/Get, the redirect part is performed by the view, not the controller as often people tend to do.


Highly subjective bit:

Lately I have preferred to interact with MVC using following approach:

  // the factory for services was injected in constructors
  $controller->{ $method.$command }($request);
  $response = $view->{ $command }();
  $response->send();

The $method is the current REQUEST_METHOD, that has been adjusted fake a REST-like API, and the $command is what people usually call "action". The controller has separate routines for GET and POST (an other) requests. This helps to avoid having same if in every "action".

And on the view I call similarly named method, that prepares a response that is sent to the client.

Warning:I suspect that this setup contains an SRP violation. Adopting it as your own might be a bad idea.


What about DRY?

As you might have noticed already, there is a slight problem with having views as instances. You would end up with repeating pieces of code. For example: menu or pagination.

Lets look at pagination .. The pagination contains logic, but this logic is not related to the model layer. The model has no concept of "page". Instead this bit of logic would reside in the UI layer. But if each of your views contains or inherits pagination, then it would be a clear violation of SRP (and actually several other principles too).

To avoid this issue you can (and should, IMHO) introduce presentation objects in your views.

Note: while Fowler calls them "presentation models", I think that name just adds to the whole 'what is model' confusion. Therefore I would recommend to call them "presentation objects" instead.

The presentation objects deal with repeated pieces of logic. This makes the views much "lighter", and in some aspects starts to mirror the structure of services from the model layer.

The interaction between presentation objects and templates becomes similar to the interaction between domain objects and data mappers.


Do I always need all of this?

No. This specific approach is heavily geared towards code, where the UI layer has a lot of complexity and you need to separate the handling of input from presentation just to sane sane.

If your application has very simple UI, like .. emm .. you are making REST API for a larger integrated project. In such the pragmatic option can be to just merge every controller-view pair into single class.

It also can be a good step, when refactoring a legacy codebase, because this less-constrained approach lets you move entire chunks of old code. When you have isolated such pieces of older code and checked, that everything still works (since legacy code never has any tests .. that's how it becomes "legacy"), you then can start splitting it up further, while focusing on separating business logic from UI.


P.S. I myself am still struggling with figuring out a way how best to deal with views. This post is less of an answer and more like a snapshot of my current understanding.

Wednesday, March 31, 2021
 
jcubic
answered 7 Months ago
44

Your code is evaluating like this:

while (!($apns = ($this->getApns($streamContext) && ($z < 11))))

which is why you see the infinite loop (as soon as $z >= 11, $apns is false, so the condition is always true). The reason for this precedence is that the special rules only apply to ! on the left of the assignment being valid (having lower precedence than =). It has no effect on the boolean operator on the right, which behaves as it would in any sane language.

Your style is bad. Try this, which is much more readable and only differs in the final value of $z (and if that's important you can tweak the break statement.

for( $z = 1; $z < 11; ++ $z ) {
    // note extra brackets to make it clear that we intend to do assignment not comparison
    if( ($apns = $this->getApns($streamContext)) ) {
        break;
    }
    myerror_log("unable to conncect to apple. sleep for 2 seconds and try again");
    sleep(2);
}
Wednesday, March 31, 2021
 
Nate
answered 7 Months ago
46

Ok I'm just gonna start by saying I'm no expert in PHP and I recommend using a framework such as symphony to do your routing, but here is one possible solution that you could use.

function regexPath($path)
{
    return '#' . str_replace([":int:", ":string:"], ["d+", ".+"], $path) . '#';
}

function parseRequestedView($url)
{
    $ressource_requested = explode('/', trim($url, '/'));


    // define our routes, and the indices that each route will use (from the exploded url)
    // this could be defined as another parameter or as a member of the class
    $routes = [
        regexPath("article/:int:/comments/show") => [0,  2, 3,  1], // will return array(resource[0], resource[2], resource[3], resource[1]), etc
        regexPath("article/:int:/show")          => [0, -1, 2,  1], // -1 will return a null
        regexPath("archive/show")                => [0, -1, 1, -1]
    ];


    // go through each route, checking to see if we have a match
    foreach ($routes as $regex => $indices)
    {
        if (preg_match($regex, $url))
        {
            // it matched, so go over the index's provided and put that data into our route array to be returned
            foreach ($indices as $index)
            {
                $route[] = $index > -1 ? $ressource_requested[$index] : null;
            }

            // include the post data (not really nessesary)
            $route[] = $_POST; // unnessesary to pass $_POST data through function, because it is global

            return $route;
        }
    }

    return null; // or some default route maybe?
}

$route = parseRequestedView("article/13/comments/show");

echo '<pre>';
print_r($route);
echo '</pre>';

/* returns:
Array
(
    [0] => article
    [1] => comments
    [2] => show
    [3] => 13
    [4] => Array // this is our $_POST data
        (
        )

)
*/
Wednesday, March 31, 2021
 
PLPeeters
answered 7 Months ago
13

I personally subclass both Zend_Db_Table_Abstract and Zend_Db_Table_Row_Abstract. The main difference between my code and yours is that explicitly treat the subclass of Zend_Db_Table_Abstract as a "table" and Zend_Db_Table_Row_Abstract as "row". Very rarely do I see direct calls to select objects, SQL, or the built in ZF database methods in my controllers. I try to hide the logic of requesting specific records to calls for behind Zend_Db_Table_Abstract like so:

class Users extends Zend_Db_Table_Abstract {

    protected $_name = 'users';

    protected $_rowClass = 'User'; // <== THIS IS REALLY HELPFUL

    public function getById($id) {
        // RETURNS ONE INSTANCE OF 'User'
    }

    public function getActiveUsers() {
        // RETURNS MULTIPLE 'User' OBJECTS            
    }

}

class User extends Zend_Db_Table_Row_Abstract {

    public function setPassword() {
        // SET THE PASSWORD FOR A SINGLE ROW
    }

}

/* CONTROLLER */
public function setPasswordAction() {

    /* GET YOUR PARAMS */

    $users = new Users();

    $user = $users->getById($id);

    $user->setPassword($password);

    $user->save();
}

There are numerous ways to approach this. Don't think this is the only one, but I try to follow the intent of the ZF's design. (Here are more of my thoughts and links on the subject.) This approach does get a little class heavy, but I feel it keeps the controllers focused on handling input and coordinating with the view; leaving the model to do the application specific work.

Tuesday, July 27, 2021
 
Juriy
answered 3 Months ago
51

In my understanding, the scope of $a and $b should end within the curly braces { }!

Only functions and methods have their own, local scope. Other control structures (loops, conditions...) do not.

Variable scope in the PHP manual

Thursday, August 5, 2021
 
ritch
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :