I'm trying to understand and figure out a good way to switch between controllers in my custom framework. The following example is what I'm currently thinking, simplified for demonstration purposes, but I would really appreciate some expert advice if there is a better approach?
class BaseController() {
function __construct() {
$this->model = new ModelFactory();
$this->view = new View();
if(isset($_SERVER['QUERY_STRING'])) {
list($controller, $action) = explode('=', $_SERVER['QUERY_STRING']);
self::process($controller);
}
}
public function process($controller) {
switch($controller) {
case 'user':
$user = new UserController($action);
break;
case 'forum':
$forum = new ForumController($action);
break;
default:
// use base controller
switch($action) {
case 'contact':
$this->view->load($action);
break;
}
}
}
}
// inside UserController.php
switch($action) {
case 'register':
break;
case 'login':
break;
}
// inside ForumController.php
switch($action) {
case 'new_thread':
break;
case 'edit_post':
break;
}
This is really a partial answer that will hopefully give you some good pointers. I'm sure someone with a better answer will come along.
Your BaseController in your example is probably misnamed. What you have in it makes it look more like a controller factory than a base controller all other controller classes might derive from. It looks like this is more of a "routing" class, so you should consider giving it a more appropriate name for its job.
If you want your framework users to create custom controllers with custom actions:
a) You'll definitely want to create at least an interface for all controller classes to implement. Call it IController or something like that. This is used in the next step.
b) You'll have to settle for creating objects using strings as classnames. IE $controllerObject = new $controller(); within your "Route" handler class. The reason being is that controller and action names to run come straight from the request URL. There's ways of aliasing this part, but that's another question entirely. Do not forget to validate and/or whitelist these "controller" class names passed in from the client. To validate: use the PHP functions class_exists($controller) and then if true, check to make sure the controller class implements IController using PHP's built-in class_implements($controller). Only then should you do $controllerObject = new $controller(); to actually create the controller object.
Your "Route" process method then becomes something more like (and keep in mind this is a very simplified example):
public function process($controller, $action) {
if (!class_exists($controller)) {
throw new Exception('Controller class does not exist.');
}
if (!in_array("IController", class_implements($controller))) {
throw new Exception('Route is not a valid controller.');
}
if (!method_exists($controller, $action)) {
throw new Exception('No such action for requested controller.');
}
$ctrl = new $controller();
return $ctrl->$action();
}
c) Do not have your controller declare any method (ie named with the value of whatever $action may be) you do not want the client to execute directly using this above design pattern. Hopefully your framework users understand this as well. Just be sure to properly document how this works internally to make your framework users aware.
There is of course way more to it than that, but that's up to you - the framework designer. Also, the action should have the final say in what "view" to use. There's ways to set defaults if the action doesn't explicitly spell out the view to use though. But again, that would be for another question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With