Views as objects

In the current system, views are directly bound to the controller, so quite often you end up with views like this.




<?php

/*

 * User view file

 * @var User $user

 * @var CController $this

 */

?>

<h1><?php echo CHtml::link(CHtml::encode($user->name),array("/user/view", "id" => $user->id))); ?></h1>

<p>Some Text</p>

<?php

if ($user->status == "active") {

    echo "This user is active";

    if ($someOtherCondition) {

        // do something else

    }

}

else {

    echo "This user is not active";

}

?>

<br />

<?php

    echo CHtml::link("Some Label", "#fish");

    echo "Current Controller ID: ".$this->getId();

    $this->widget("myCustomWidget", array("foo" => "bar"));

?>



I think it would be useful to introduce a specialized View class so that this kind of logic, which relates only to one or two specific views can be encapsulated outside of the template itself. This makes it easier for designers to work with views, and for views to be localized. The base view class could also provide access to the current CHtml methods, which would make it easy to replace CHtml with your own class, solving a lot of the issues regarding supporting different CSS frameworks. Importantly, it also makes it much easier to test your views without relying on selenium.




<?php

/**

 * User view helper

 */

class UserView extends CView {

    public $someOtherCondition = true;

    public function displayUserStatus($user) {

        if ($user->status == "active") {

            return "This user is active".($this->someOtherCondition ? $this->doSomethingElse() : "");

        }

        else {

            return "This user is inactive";

        }

    }

    public function doSomethingElse() {

        return "foo";

    }

    public function userLink($user) {

        return $this->link($this->encode($user->name),array("/user/view", "id" => $user->id));

    }

}

?>


<?php

/**

 * User view file

 * @var User $user

 * @var UserView $this

 */

?>

<h1><?php echo $this->displayUserLink($user); ?></h1>

<p>Some Text</p>

<?php

echo $this->displayUserStatus($user);

?>

<br />

<?php

    echo $this->link("Some Label", "#fish");

    echo "Current Controller ID: ".$this->controller->getId(); // still provide access to the controller if required

    $this->widget("myCustomWidget", array("foo" => "bar")); // views can render widgets just like controllers do now

?>



There are models for things like that.

Presentation logic does not belong in the model

It belongs in the controller.

Or, in a widget.

Or even a helper.

Why can’t $this->displayUserStatus($user); be a controller function?

I would put it in a helper/component, or - if only used once or twice - in the controller.

I like how flexible it is currently.

Turning the view into an object is unnecessary.

It would require more memory. For what?

I think phpnode is trying to propagate a MVP or MVA paradigma for Yii 2 :rolleyes:

It would have its merrits. But it were no longer MVC.

Because that breaks the separation of concerns: models deal with manipulating data, views deal with displaying data, controllers just decide what model to display with which view. The controller shouldn’t affect the presentation. Besides it’s perfectly reasonable to use the same view with different controllers, so which controller should displayUserStatus() go in? The only real candidate is a helper class, but why not make that helper class part of the view?

I’m not saying we drop MVC at all, I’m saying that we should flesh out the V so that it’s more in line with the original definition of MVC, with the view controlling the presentation rather than just being a template. This allows views to be extended and reused more easily, at the moment Yii is more like MTC - model template controller. Creating a custom view class would be totally optional, it would use the CView base class by default, which would leave things almost exactly the same as they are now, the only difference being that $this refers to the view, rather than the controller, and you no longer need static method calls to CHtml for generating text boxes and links etc.

This subject has been discussed here already.

I agree this can be handled in the controller. Doing this would just complicate the application.

Widgets and clips is made for the purpose of using the same view with different controllers.

Er, did you write ‘using the same view with different controllers’ ?

Wow, that would really fuck things up, wouldn’t it?

I can’t really see any benefits of having a view object.

Why can’t $this->validateUserInput($_POST); be a controller function? Having a real View layer in your applications has exactly the same advantages as having a Model layer.

No it does not totally fuck things up, its a very common use case, eg displaying a list of books for an author you might use views/book/_view.php in your list view, even though the current controller is AuthorController

Yes, but it’s usually included from the active controllers view file?

I use widgets for that.

Yea in those use cases I usually use widgets too or have a "global" templates folder somewhere for reusable views.

Object views are not a good idea. Right now you have access to the controller right away and it has it’s benefits (I have a use case when I have a custom controller level variable initialized in “init” witch the views needs - with view object I will have to pass it all over the place and that’s not helping).

Over-engineering stuff is not healthy, widgets and clips are taking care of re-usability extremely well. I believe it should stay that way, the simplicity of the system is what makes Yii so good, it does not make you use object to simply render a view and that’s great (and I have to remind about the beginContent/endContent and other functionality that controller has, some of it can just not work inside a view object. And it’s damn useful - too good to be removed just to make a object view.

KISS.

I think phpnode is not so much into reusability. To me it looks like he just wants views that are easier to test. This is actualy a strong point in Model-View-Presenter.

You wouldn’t have to pass anything round at all, instead of using:




$this->foo();



You’d just use




$this->controller->foo();



It’s not so painful I think.

I’m explicitly saying that you would never have to create a new View object, you could happily use the default view object and continue to put your logic in your controllers if that’s what you want. So something like this




// in the controller

public function actionIndex() {

    $this->render("index", array("foo" => true));

}

public function doSomething() {

    return "blah";

}


// in the view file

if ($foo) {

    echo $this->controller->doSomething();

}



it would still work. But if you do want to extend the default view, you’d do something like:




// in the controller

public function actionIndex() {

    $view = $this->createView("MyCustomView");

    $view->render("index", array("foo" => true));

}


// in the MyCustomView class


public function doSomething() {

    return "blah";

}


// in the template file

if ($foo) {

    echo $this->doSomething();

}



We wouldn’t need to remove them. The point is you still have access to the controller if you need it via $this->controller.

I agree that it is really not MVC that Yii is using.

However, I am not totally sure I like it…

I mean, I prefer MTC.

I think one of the benefits of MTC over MVC is that you don’t create as many objects and thus doesn’t waste as much CPU and RAM.

I think that’s why Yii is faster than /(¤&#"

For testing, couldn’t the controller be mocked instead?

If that’s the issue?

Still need to see something really convincing…

?

I tend to agree to phpnode. While you can discuss a lot about how to implement such a view class in detail, to me it’s quite clear that in the current implementation the controller deals way to much with the “V” part of MVC. We use objects for pretty much everything, but we suddenly leave this path when it comes to the View part. With a view class we could benefit from the usual OOP advantages. It would just be consequent to move all rendering logic and view related methods into the view domain.

But true, we already discussed this. Maybe we should revive the old thread instead.

I am a bit of a newbie when it comes to Yii and MVC but after reading everybodies thaughts on this I think we need to take a step back and define what we currently have and what want to get out of changing it.

Firstly, what is a View?

My understanding is that a view is for markup and layout and basic presentation stuff, not for logic.

So where can we put re-usable logic?

Widgets and Clips, controller methods, model methods, application helper functions, user session objects.

Any more?

The way I see it you looking for a preprocessing layer to a View where you can put logic, but then you have to ask yourself should this layer be able to alter variables/data being passed into the View from the Controller. And what about themes, are they also allowed to have logic or override the View logic?

I personally don’t think another layer is necessary there are so many options already.