Controller in MVC

What does controller actually do in MVC ?
Is it just instantiating objects and giving some orders ?

Is this one below a right implementation ?

class IndexController {
	private $dbh;

	public function __construct() {
		$config = new Config();
		$this->dbh = new PDOAdapter($config);
	}

	public function launch() {
		$user = new User($_SESSION['user']);
		$usertime = new Datetime('now',$user->getTimeZone());
		$postRepository = new PostRepository();
		$posts = $postRepository->paginate($request->get('page'), 5);
		$widgetRepository = new WidgetsRepository();
		$widgets = $widgetRepository->getActiveWidgets();
		$templateRepository = new TemplateRepository($this->dbh);
		$template = $templateRepository->getCurrentTemplate();
		$pluginRepository = new PluginRepository();
		$plugins = $pluginRepository->forIndex();
		$plugins->run($posts, $template, $widgets, $template);
		$template->view();
	}
}

A controller can send commands to the model to update the model’s state (e.g., editing a document). It can also send commands to its associated view to change the view’s presentation of the model (e.g., by scrolling through a document).

You can think of the controller as the link between your model and your view, used to carry out actions between the two.

Whether or not your code is a correct implementation of a controller depends on what all those methods and classes do and also how the framework calls on the controller. What I am missing is the extension of the base controller, which usually gives you access to a lot of the framework’s core features and is usually standard in most of the frameworks I’ve seen.

Laravel
Symfony
Yii
Codeigniter

Scott

1 Like

I think Symfony has the cleanest and most framework-agnostic definition: “A controller is a PHP callable you create that takes information from the HTTP request and creates and returns an HTTP response. … The controller contains whatever arbitrary logic your application needs to render the content of a page.”

I also like the article From Flat PHP to Symfony2 for learning MVC. The first half of that article is completely framework-less, and it takes you step by step to refactor spaghetti PHP into MVC.

1 Like

According to that, you could stick all your logic in your controllers and it would still fit their definition, but it wouldn’t be MVC (nor good practice).

That’s absolutely true. I was debating whether to write more detail, but decided the OP already seemed to know about and use repositories and templates. But you’re right. With that Symfony definition, it makes more sense to talk about what the controller doesn’t do – at least not directly. Any business logic should be factored out into entities, repositories, persistence, etc., collectively called the model. And response body content should be factored out into templates, collectively called the view. Ultimately a controller invokes both those things to complete its task of producing an HTTP response.

But nonetheless, if we’re going to pick a simple definition, I still like Symfony’s the best. The other framework descriptions that molinari linked don’t seem clear about a controller’s responsibility. They either describe just the mechanics (a controller is a class that extends CController), or they’re vague (controllers group “route logic” into a class), or both (a controller is a class named in a way that can be associated with a URI).

Symfony’s definition is the only one that says what a controller is actually supposed to accomplish: Given an HTTP request, a controller produces an HTTP response.

1 Like

Should the controller be classed ?
Why don’t just give the app logic in a file such as index.php ?

There are different patterns to build a web application in PHP. One is the flat file option, which is what you are suggesting. This is ok, but you end up with “ugly” or parameterized URLs. Like, http://www.yoursite.com/index.php?do=something

Another option is the frontend controller pattern, which basically forces the requests to the application to enter through a single file e.g. with Symfony it is “app.php” for a production site (or app_dev.php for a development site). This is how most of the bigger frameworks work and allows for “pretty” or semantic URLs.

So, if you want to have pretty URLs, you will need to use the frontend controller pattern. This also allows you a better opportunity to “class your controller”, as you put it, which is also an advantage towards the modularity of your code and testing.

Scott

1 Like

It doesn’t have to be. In fact a lot of new frameworks are allowing or even requiring that the controller be a closure function. But grouping some controllers (usually related CRUD actions) together into a class has some benefits. It lets you consolidate similar code of similar actions into a shared private method, and it lets you extend a base controller.

[quote=“terrydoang, post:6, topic:189933, full:true”]Why don’t just give the app logic in a file such as index.php ?
[/quote]

I think the Flat PHP to Symfony2 article can help explain that:

“What if each controller file suddenly needed to include an additional file or perform some other global task (e.g. enforce security)? As it stands now, that code would need to be added to every controller file. If you forget to include something in one file, hopefully it doesn’t relate to security… The solution is to use a front controller: a single PHP file through which all requests are processed.”

2 Likes

This is actually anti-MVC. In MVC the controller is not a mediator between the view and the model. From wikipedia:


As you can see, the controller does not manipulate the view at all. Most “MVC” frameworks actually use “PAC” see:

In MVC proper, the controller updates the model, and then the view requests the state from the model. Where this becomes particularly useful is where the model’s state is shared between views (I wrote an article discussing exactly this here: https://r.je/mvc-tutorial-real-application-example.html )

The problem with the controller as mediator approach is that it creates a lot of extra binding logic. Rather than just having the view request data from the model, the controller has to request data from the model just to pass it on to the view. This kind of pass-the-parcel architecture is needlessly inflexible. It means whenever the model is changed and a new variable is needed in the view then the model, view and controller all need updating, in MVC proper, only the view and model would.

1 Like

MVC proper was designed for rich, stateful, GUI applications, which the web is not. The defacto adaptation of MVC for the web that modern frameworks use I believe originally came from Fowler.

But why call it MVC? It’s needlessly confusing to have two very different architectures with the same name. Not only that, MVC, in its original definition, is perfectly suited to the web, in some ways it’s even more fitting on the web because there’s no point where the view needs to be refreshed. MVC is just a formal separation of concerns, that separation of concerns is equally applicable on the web.

As far as I can tell “Web MVC” simply came as a renaming of an architecture that had been in use for years: DAO + Entry Point + Template system and people just appropriated the term “MVC” to make it sound better designed. Needless to say, calling it MVC is at best confusing.

Yeah, that image sort of got me wondering. Would you consider this image more appropriate to say, Symfony’s not-so-MVC-MVC?

Scott

To be fair, last I checked, Symfony doesn’t call itself MVC, but yes, the architecture in that image is certainly PAC rather than MVC because the controller is is acting as a gateway between the model and the view which architecturally is significantly less flexible than having the view read data from the model directly, at its most basic level it means you cannot have a request without a controller. Using MVC if the request needs to do nothing apart from display some static data from a model, there’s no need for a controller, the view is instantiated, passed the model instance and rendered. Using the above, you must instantiate a controller every time.

The architecture in that image is the architecture for most “MVC” frameworks and as such, isn’t really MVC.

The only thing I could find is a sentence where they say they follow the MVC philosophy.

I am trying to picture what more flexibility would look like. I am also trying to imagine how the model would push changes to the view and if that is any better or worse than the controller doing it, since the controller knows what is expected as the response and knows the manipulations made to the model too. Is the flexibility really needed?

Scott

In this case, the view pulls from the model rather than having data pushed into it.

We can turn that around: When is flexiblity a bad thing?

Consider a basic blog. E.g. this one from: http://tutorial.symblog.co.uk/docs/extending-the-model-blog-comments.html#the-controller

class PageController extends Controller
{
    public function indexAction()
    {
        $em = $this->getDoctrine()
                   ->getEntityManager();

        $blogs = $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy('b.created', 'DESC')
                    ->getQuery()
                    ->getResult();

        return $this->render('BloggerBlogBundle:Page:index.html.twig', array(
            'blogs' => $blogs
        ));
    }

    // ..
}

There are a multitude of flexibilty (and by extension reusability) issues here.

1) There is no real user-action here.

This is displaying a static set of data from the model. This could be rewritten without the controller entirely if it was set up like this:

new View('BloggerBlogBundle:Page:index.html.twig', new PageModel);

and the view called a method on the model:

foreach ($this->model->getPages()) {
	//...
}

and the model instead had the application state

class PageModel {
	public function getPages() {
		$em = $this->getDoctrine()
                   ->getEntityManager();

       return $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy('b.created', 'DESC')
                    ->getQuery()
                    ->getResult();
	}
}

there is zero need to even instantiate the controller

which brings us neatly to…

2) The application state is stored in the controller rather than the model

This causes a problem when I want to reuse the data selection elsewhere in the application, anywhere I want to display news articles I need what is basically the same controller copied/pasted. Consider some search/sort Options.

class PageController extends Controller
{
    public function indexAction()
    {
        $em = $this->getDoctrine()
                   ->getEntityManager();

        $blogs = $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy('b.created', $_POST['sort'])
                    ->addWhere('b.title LIKE ' . $_POST['searchCriteria'])
                    ->getQuery()
                    ->getResult();

        return $this->render('BloggerBlogBundle:Page:index.html.twig', array(
            'blogs' => $blogs
        ));
    }

    // ..
}

Instead, if this was in the model:

class PageModel {
	
    public $sort;
    public $where;
	

	public function getPages() {
		$em = $this->getDoctrine()
                   ->getEntityManager();

       return $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy('b.created', 'DESC')
                    ->getQuery()
                    ->getResult();
	}
}

then the controller would use the model and not be tied to the underlying data source or the view:

class PageController {
    private $model;

    public function __construct(PageModel $model) {
        $this->model = $model;
    }

    public function indexAction() {
        $this->model->sort = $_POST['sort'];
        $this->model->where = 'b.title LIKE ' . $_POST['searchCriteria'];
    }
}

This alone has the advantage of decoupling the user input from the model. I can now use this controller with a model that’s pulling data from another source.

Where this becomes particularly useful is where you have repeated functionality. Consider tabular data or forms. With tabular data there is a lot of repeated functionality:

  • Change the page
  • Sort by a given column
  • Change the number of results per page

By separating out the application state from the model state it allows the removal of this repetition by having a reusable controller that can take a model that follows a specific interface


interface TabularData {
    public function getData();
    public function setSort($field, $dir);
    public function setSearch($field, $value);
    public function setPage();
    public function setRecordsPerPage();
}

class TabularDataController {
    private $model;

    public function __construct(TabularData $model) {
        $this->model = $model;
    }

    public function sort() {
        $this->model->setSort($_POST['sortField'], $_POST['sortDir']);
    }

    public function page($num) {
        $this->model->setPage($num);
    }

    //...
}

Then I can write any number of models which use the interface:

class PageModel implements TabularData {
    private $sortField = 'name';
    private $sortDir = 'desc';
    private $searchField;
    privvate $searchValue;
    private $page = 1;
    private $

    public function getData() {
        $em = $this->getDoctrine()
                   ->getEntityManager();

       $result = $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy($this->sortField, $this->sortDir);
                    
        if ($this->searchField) $result->addWhere($this->searchField  . ' LIKE ' . $this->searchValue);
        return $result->getQuery()->getResult();
    }


    public function setSort($field, $dir) {
        $this->sortField = $field;
        $this->sortDir = $dir;
    }


    public function setSearch($field, $value) {
        $this->searchField = $field;
        $this->searchValue = $value;
    }

    public function setPage($num) {
        $this->page = $num;
    }
}

It’s then possible to add models for users, pages, cagegories or anything else that may have tabular data by providing the model to the relevant view:

$model = new PageModel;
$controller = new TabularDataController($model);
$view = new PageView($model);

The view then calls

foreach ($this->model->getData()) {
    
}

Taking it a step further, if the model had a getHeadings() method, the same view could be reused for any kind of tabular data.

3) The view is hardcoded in the controller.

When the view is hardcoded in the controller it makes it difficult to substitute without repeated code. By selecting the view outside the controller it’s easy to do:

$model = new PageModel;
$controller = new TabularDataController($model);
$view = new PageRssView($model);
$model = new PageModel;
$controller = new TabularDataController($model);
$view = new PageJsonView($model);

With this implementation

  • I can now use a different controller with the same view (e.g. one that used GET rather than POST, or fetched the search options from somewhere else like a user’s preferences)
  • I can substitute the model so data is retrieved from a different data source
  • I can easily substitute the view

All of this becomes difficult when the controller is doing all the work, you either end up with if/elses rather than polymorphism or you end up with repeated code

1 Like

Great stuff Tom. And I actually understood it. LOL! :grinning:

Though, somewhere, somehow, we need to “control” the process flow from a request to the response. Currently most frameworks do this with “controllers”. Most good developers constantly preach about having “thin controllers” and “fat models”, which is basically what you suggest by putting model logic in the model.

But still, how can a process flow work without some sort of “manager”. I don’t think setting direct dependencies is the answer and I agree, there can be a lot of code duplication with (fat) controllers too. Though, this code

$model = new PageModel;
$controller = new TabularDataController($model);
$view = new PageRssView($model);   

in a “manager” is still a coordinating link between the model and view…and now the controller.

There always needs to be something that drives the process flow. In Symfony, it is the controller. Looking around for something that might be an alternative, I ran into this interesting blog series.

Scott

That’s a bit of a red herring. You already have a router that says “/products/* maps to ProductsController” and instantiates the controller. It’s as simple as replacing

$routes['page/*'] = 'PageController';

with:

$routes['page/*'] = ['WhateverController', 'PageModel', 'PageView'];

and updating the router accordingly.

edit then you can add the rest of the routes:

$routes['page/rss/*'] = ['WhateverController', 'PageModel', 'PageRssView'];

Why a red herring? Aren’t you suggesting the front controller be that “manager” I consider is always needed for process flow?

Scott

What I mean is that it’s not really a problem that doesn’t exist anyway. At some point you must instantiate the controller using some logic to determine which controller to use, the model and view are no different so just instantiate them where you instantiate the controller and you get improved flexibility.

It seems so simple, and I agree with what you are saying, but why isn’t it done this way? Or rather, is there a framework out there that does “the managing” (or maybe “coordination” is a better word?) similarly?

Scott