2
\$\begingroup\$

I am currently trying to implement a very simple MVC framework as a way of helping me understand how they work. My base controller class has 2 properties, a model and a view. All controllers have a generic view class property however only certain controllers have a model.

I needed a way of obtaining the name of the child controller class in the base controller class so that in the constructor I can check to see if a model class exists and instantiate one if so. I think I have come up with a solution using late static binding. I do not fully understand the concept, but as far as I can tell, if used in a parent class the static keyword refers to the child class.

I also implemented a function className() in the controller class which uses the 'this' keyword to get the name of the current class. This function is then called with the static keyword resulting in the name of the child class being obtained.

Below is the code for the Controller class. All models are titled controllerName_model.

/**
* Base controller class, creates a generic view class and a model class (if one exists for the child class) for the controller.
*/
class Controller
{
    public $view;
    public $model;

    public function __construct()
    {
        //Create a new genereic view object
        $this->view = new View();

        //Use late static binding to get the name of the child class extending this base class
        $name = static::className();
        //Create the path to the related model (if it exists)
        $path = 'models/' . $name . '_model.php';

        //If the model exists then require the file and create a new model object and store in $this->model
        if(file_exists($path)) {
            require $path;

            $modelName = $name . '_Model';
            $this->model = new $modelName();
        }
    }

    /**
    * This class returns the name of each class that extends this base controller class. It is used in the 
    * constructor with late static binding to get the name of the child class so that a model can be created if one exists.
    */
    public function className() {
        return get_class($this);
    }
}

As far as I can tell this method is working but I am not confident that it is correct or that it is the best way of doing this. If anyone knows of any other ways of doing this or can point out any flaws in this approach I would be extremely grateful. Thanks.

\$\endgroup\$
2
  • \$\begingroup\$ get_class($this); But "that in the constructor I can check to see if a model class exists and instantiate one if so" makes no sense \$\endgroup\$
    – zerkms
    Apr 25, 2013 at 12:29
  • \$\begingroup\$ Basically I need the name of the child controller within the main Controller class. Once I have the name of the controller I can then check if there is a related model class. The model classes are named the same as the controller but with _Model after the name, e.g. there is a controller class called Dashboard, and the corresponding model is Dashboard_Model. If the model exists I create a new model class and assign it to the $model property of the controller class. \$\endgroup\$ Apr 25, 2013 at 14:26

2 Answers 2

1
\$\begingroup\$

Looks fine to me; though somethings to think about. Do you need to run get_class? Or might it be better to have a model variable in the child class

class Blog extends Controller
{
    private $modelClass = 'Blog';
}

The overhead of get_class might be small, but probably more than a variable; but this way also give you the ability of being able to use the same model with multiple controllers if you need to. Granted you could just inherit with the models too, but that would mean 2 includes instead of one.

If you don't go that route, consider some validation, string tidying or consistency in your model/file names.

$path = 'models/' . $name . '_model.php';

will likely equal Blog_model.php but your class is Blog_Model see the mixing of cases; consider Blog_Model.php or doing a strtolower($name). This might seem minor, but different Operating Systems handle case differently and I have been bitten by this exact issue.

I noticed you are doing a require rather than a require_once which could lead you to have cannot re-declare class errors. if require_once isn't for you consider

if(!class_exists($modelName))

This might seems abstract at the moment, but when I came to upgrade an admin system for a custom CMS I found it really handy for the admin Controllers to wrap the frontend wesbites controllers, and adding a little checking could greatly increase your flexibility further down the road.

If you haven't done already check out __autoload http://php.net/manual/en/language.oop5.autoload.php if might be more overhead, but it is just so handy I really miss it on legacy projects.

\$\endgroup\$
1
\$\begingroup\$

First of all, none of so-called "mvc frameworks" in php actually implement MVC or any other MVC-inspired design pattern. Instead they attempt to mimic Rails (which is primarily a rapid prototyping framework).

The other thing that you seem to be unaware of is that model in proper MVC is not a class or an object. Model is a layer, that contains all of the business logic. It is one of the two major layers in MVC. Other being the presentation layer, which contains view, controller, templates and some other types of structures.

  • for more details on the subject: read GUI Architectures by Martin Fowler,
  • to see how it might apply to PHP project, take a look at this answer.

.. now the "review part"

  • avoid the use of public attributes for objects. This breaks the encapsulation. If you expect to extend the class, then instead you should use protected visibility.

  • constructors should not do any "work". If you put complicated logic in constructors, it makes it harder to test you code. Instead, if there is some code that must be executed before the instance is released in "general population", you should use builders or factories for that.

  • do not instantiate objects in the constructors. This too makes it harder to test or even recognize the source of bugs. Instead you should pass the required instance in the constructor. This practice is called "dependency injection"

  • if you read wiki article on MVC, you will notice, that loading class files is not one of controller's responsibilities. This task instead should be done by an autoloader. I would recommend for you to explore the use of spl_autload_register(). For simplified implementation example you can take a look at PSR-0 ... though, do not take it as gospel.


As for the application design ..

I already mentioned above, but lemme reiterate: the controllers in MVC have quite precise responsibilities. They handle the user input. In web applications every change in the site is a direct result of user's action.

These controller alter the state of model layer, usually by employing services, which isolate the business logic from presentation layer. Controllers do not acquire data from model layer.

That part is done by view instances. Each view instance is responsible for all of the UI logic in the page that you see (if it is sending HTML as response .. there are exceptions). It assembles the the response from multiple templates based on data that it acquired from model layer. The controller have almost no contact with views. The most that controllers do is alter the type of response that view produces (switching from html to json or xml).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.