PHP MVC - How can Controller and View access same

2019-06-13 14:23发布

问题:

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.

回答1:

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.



回答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



回答3:

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.