Struggling with MVC in PHP. My concerns grew even bigger after watching this: https://www.youtube.com/watch?v=RlfLCWKxHJ0
According to LoD my Router class should only know the Request Uri to load proper Controller class. However my Conroller should know which Model class it should use and a View class to present data. Or better, Controller should know ModelFactory that will handle object creation using selected data storage.
This all breaks LoD to me.
So my question is:
Maybe this is all wrong, but my starting point is:
// ... retrieve settings, available languages, start session,...
$router = new Router($settings);
$router->loadController();
Router.php
class Router
{
public function __construct(Settings $settings)
{
$this->settings = $settings;
}
// some other methods
public function loadController()
{
try
{
// Loading controller
$controller = $this->getController();
if (is_callable(array($controller, $this->method)) == false)
$this->method = 'init';
// Running controller
$controller->{$this->method}();
}
catch (Exception $e)
{
$e->displayMessage();
}
}
}
And from here on I can do nothing inside my Controller class, cause I need to call new Model, new View and have to do it explicitly inside constructor or method, which is bad.
More Questions:
First, the model is a layer, not a specific class. The controller itself will initiate classes necessary to start processing the business logic of your application.
Take a look at these answers. The 2nd has some examples of how these classes may be called...
How should a model be structured in MVC?
Properly calling the database from Model in an MVC application?
Second, the router just routes the requested url to a specific controller and view. There are different ways to do it, but my router(s) check from matches against an existing resource map. Based on a few other things, it will either return a successful "resource" name for the page, a resource name for pages not found, or resource for redirection, etc.
Some basic code from a bootstrap for illustration...
//snip
$routeLoader = new \Routing\RouteLoader();
$match = $routeLoader->getMatchedRoute( $request['pageName'] );
$router = new \Routing\Router( $request );
$resource = $router->getResource( $match );
//snip
$viewName = '\View\\' . $resource . 'View';
$controllerName = '\Controller\\' . $resource . 'Controller';
$view = new $viewName();
$controller = new $controllerName( $view, $request );
$controller->{$router->getCommand()}();
$view->response();
So, to directly answer your questions..
1) Do not initiate the controller and view inside the router class. Do it in the bootstrap.
Also, for web applications, you may or may not need a DI, a service factory, or even find it necessary to use a specific design pattern. For most, I think its overkill and adds unnecessary complexity.
2) The controller will initiate classes to start processing the business logic (if its even necessary.)
ADDITIONAL
To add a database connection to the above and inject it into the controller, below is 1 way to do it..
$DCM = new \Database\DatabaseConnectionManager( new \Config\DatabaseConfig() );
$AppCache = new \Cache\AppCache();
$DAM = new \DataAccess\DataAccessManager( $AppCache, $DCM );
In the above code, the DataAccessManager object would be responsible for retrieving data from the either the cache first, or the database second. This $DAM object can now be injected into the controller, like so...
$controller = new $controllerName( $DAM, $view, $request );
Instead of connecting to the database in the bootstrap and passing the connection around the application, I prefer to use a DataAccessManager, which will make the connection only when it is actually needed. Once it is needed, a PDO object (or whatever) is initiated and stored in the object to be retrieved and used again if necessary. I can also make connections to other databases if necessary..
// method from DataAccessManager class
private function connectToDatabase( $server = 'slave' )
{
if (!array_key_exists( $server, $this->dbObject )) {
// use the DataConnectionManager to connect and store the connection here
$this->dbObject[$server] = $this->DCM->connect( $server );
}
return $this->dbObject[$server];
}