0

I'm developing my own PHP MVC for a database application, which I'd already completed but using monolithic classes that do everything - MySQL processing, input processing and display. It works great but it's a nightmare to debug and to make alterations! I've recently discovered MVC's and in the process of rewriting the app - it's all on the drawing board at the moment so I haven't actually got any code to share with you.

There's one thing I'm stumbling on: I've got it that the Controller should call the Model to modify it based on user input, and then call the View, which will access the Model(s) it needs to get its data and then display it. I'd like the view to gets its data independently of the Controller, and not have the Controller act as the intermediary. I started off down that line of the Controller getting its data from the Model and handing it to the View, but quickly ran into trouble where a View would need access to several Models.

To explain what I'm stuck on, I'll draw an example:


Example: submitting a form with data, which fails the Model's validation

Front controller:

  • Load the relevant controller and call the ProcessForm action

Controller:

  • Instantiate the Model class and call the methods to load and validate the data

Model:

  • validation failed: generate a list of errors and return 'false'

Controller:

  • If Model had returned 'true', redirect to the Index page
  • It returned 'false': invoke the View

View:

  • Instantiate the Model class and fetch the data
  • Display the data and the error message(s)

My problem is, how can the View, which has its own instance of the Model, get the error message(s) which were generated in the Controller's instance of the model, without the Controller handing its own Model instance directly to the View? I'm figuring the first instance would need to store the messages somewhere where the second instance could retrieve them?

I realise the first instance could return its errors to the Controller, which it can then pass to the view, independent of the Model itself, but I'd still like to keep the View independent of the Controller.

Iain
  • 307
  • 4
  • 17
  • I don't know if i understand you right but perhaps Singleton Pattern wil help you. – AlucardTheRipper Apr 07 '13 at 20:57
  • Are you using a popular MVC framework for this? I recommend you do: it is rarely a good idea to write your own. – halfer Apr 07 '13 at 20:59
  • Your views shouldn't be connecting with you models.. for that task are controllers.. – Svetoslav Apr 07 '13 at 21:00
  • @AlucardTheRipper , that is the **worst** possible solution that anyone could advise. – tereško Apr 07 '13 at 21:02
  • @Svetlio , because controller's responsibility is only to alter the state of the model layer. There is nothing said about information being returned to the controller from model layer. If your model is returning to the controller, then it is not MVC. – tereško Apr 07 '13 at 21:03
  • 1
    @halfer the app I'm making is only for personal use, I write code as a hobby; I could use a readymade framework but then I could just use a readymade app to begin with :) – Iain Apr 07 '13 at 21:14

3 Answers3

2

Well .. fists of all, there are no "models". Model in MVC and MVC-inspired design patterns is a layer, that contains multitude of structures, just like presentation layer contains controllers, views and some other things.

That part aside, your issue is that you are having separate instances of those "models". Instead both controllers and views should share same factory, which can produce and cache the instances.

The optimal solution would be for you to have services, which represent parts of model layer and through which controllers and views interact with model. A factory would initialize the service first time when it is requested, and then provide the same instance on any repeated request.

class ServiceFactory
{
    private $cache = array();

    public function create( $name )
    {
        if ( array_key_exists($name, $this->cache) === false )
        {
            $this->cache[$name] = new $name;
        }
        return $this->cache[$name];
    }
}

This is the "extremely dumbed down" version of service factory.

At the bootstrap stage you initialize the service factory and then provide both the current controller and current view with instance of it, by injecting it in the constructors of said instances.

Community
  • 1
  • 1
tereško
  • 58,060
  • 25
  • 98
  • 150
  • Thanks Tereško, it did take me a while to get my head around the Model being a layer and not an object (I'll thank several tutorials for that misunderstanding), when I say "the model" I do mean "the relevant componnent in the model". – Iain Apr 07 '13 at 21:06
  • And thanks for the ServiceFactory example: up until that example I hadn't got my head around those either! – Iain Apr 07 '13 at 21:07
  • coming back to this ServiceFactory example again. Should the $cache property be static? Am I right in thinking if it's not static, then when the loader creates a ServiceFactory instance then passes it to the controller and view, then they will both have independent copies of it? – Iain Apr 08 '13 at 16:27
  • 1
    No, the `$cache` must **not** be static. That would create global state in your codebase. As for what happens when you inject an object, take a look at [**this example**](http://codepad.viper-7.com/tRWLDY). As you can see, the object is not copied but shared. This behavior is utilized when you want to share a single DB connection between multiple objects: [example here](http://stackoverflow.com/a/11369679/727208). – tereško Apr 08 '13 at 17:00
  • Ah, that wasn't how I expected it to work. So to apply this to my original question: if the Controller modifies the model via the ServiceFactory, then when the View gets its data via the ServiceFactory it will contain what the Controller's "instance" has put in there? – Iain Apr 08 '13 at 19:56
  • The ServiceFactory looks like it is also a locator. why isn't this an implementation of the service locator anti-pattern? – jeremy Jul 25 '16 at 02:37
2

First of all there shouldn't be different model instances for controllers and views. They should all use the same instance. It's more practical to divide you classes in this structure.

Domain classes (models)

Domain classes are just holding the data to give it context. For instance you could have a Person domain class.

class Person {
    private $name;
    private $age;
    ...
    public function getName()
    public function getAge()
    ...
}

Controllers

Controllers are the bridge between the model and the view. They should not contain any business logic. That's what services are for.

class PersonController
{
    private personService;

    public function list(Bag bag) {
        bag.add('personList', personService.listAll());
        ...
        Give bag of data to the correct view
        ...
    }
}

Services

Services handle application logic. Mostly interactions between entities that contain domain logic and the storage abstractions.

class PersonService {
    public function listAll() {
        ...
        Do the logic to find all persons
        ...
        return $persons;
    }
}

Views

Views just display the data given to them by the controller. It doesn't have it's own model.


It doesn't solve all problems but at least it's a decent start to have your structure right.

The Spring Framework (Java) has a very good implementation of MVC. See for inspiration http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html

Bart
  • 17,070
  • 5
  • 61
  • 80
  • Actually, what services handle is called "application logic". That is mostly interaction between entities that contain domain logic and the storage abstractions. Also, views should request information from the model layer, because controller are not responsible for passing data from model to views. – tereško Apr 07 '13 at 21:33
0

without the Controller handing its own Model instance directly to the View

Why? This is what happens normally, and this is probably what you should also do. The model instance can store the validation errors internally, and if the controller passes the same instance to the view the problem of how to propagate this information solves itself.

Jon
  • 428,835
  • 81
  • 738
  • 806