I need to know if I am going in the right direction with my code so far. I am really trying hard to transition from procedural, page-based programming into OOP MVC. So far I have written a simple router, and MVC for updating users.
First, my index.php file acts as my bootstrapper.
index.php:
// start session
session_start();
// include config constants
include __DIR__.'/config.php';
// autoload classes via composer
include __DIR__.'/vendor/autoload.php';
// route user based on url path
$router = new router();
$router->set_properties();
$router->route();
Any URL that is visited will be turned into http://localhost/myapp/?path=controller/method/param
etc. by my .htaccess file.
For example, for the update user URL, you visit http://localhost/myapp/user/update/12
and this would point to $user_controller->update(12)
.
router.php:
class router {
public $controller;
public $method;
public $parameters = [];
function set_properties () {
if (isset($_GET['path'])) {
// set controller from path
$path = explode('/', $_GET['path']);
$this->controller = $path[0].'_controller';
unset($path[0]);
// set method from path if it exists, otherwise use "index"
if (isset($path[1])) {
$this->method = $path[1];
unset($path[1]);
}
else {
$this->method = 'index';
}
// set parameters to remaining path elements
$this->parameters = $path;
}
else {
// path not set, show app index
die('app index');
}
}
function route () {
if (method_exists($this->controller, $this->method)) {
// controller method exists
$controller = new $this->controller();
call_user_func_array([$controller, $this->method], $this->parameters);
}
else {
// controller method does not exist, show 404
die('404');
}
}
}
I am trying to keep the controller as light as possible. Here is a sample of my controller code, which includes the update
method referenced before.
controller.php:
class controller {
public $sanitizer;
public $redirector;
function __construct () {
$this->sanitizer = new sanitizer();
$this->redirector = new redirector();
}
function view ($filename) {
include __DIR__.'/views/'.$filename.'.php';
}
}
user_controller.php:
class user_controller extends controller {
public $user;
function __construct () {
parent::__construct();
$this->user = new user();
}
function update ($id) {
if ($this->user->exists_by_id($id)) {
// user exists by id, fetch and show update form
$this->user->get_by_id($id);
if (!empty($_POST) && $this->user->validate($_POST)) {
// update form posted, update user if input is valid
$this->user->update($id);
}
$this->view('user_update');
}
else {
// user does not exist by id, redirect to user index
$this->redirector->redirect('user');
}
}
}
As you can see, the controller is only handling views and redirects. The rest of the business logic (like validation, checking existence) is done by the model.
Now here is where I begin to get frustrated. Is this controller light enough? Is the model doing what it should be?
model.php:
class model {
public $pdo;
public $validator;
function __construct () {
$this->pdo = new PDO('mysql:host='.APP_DATABASE_HOST.';dbname='.APP_DATABASE_NAME, APP_DATABASE_USERNAME, APP_DATABASE_PASSWORD);
$this->validator = new validator();
}
}
user.php:
class user extends model {
public $id;
public $username;
public $email_address;
function exists_by_id ($id) {
// check if user exists by id
$stm = $this->pdo->prepare('select count(*) from `user` where `id`=:id');
$stm->bindParam(':id', $id);
$stm->execute();
return $stm->fetchColumn();
}
function get_by_id ($id) {
// set user properties by id
$stm = $this->pdo->prepare('select * from `user` where `id`=:id');
$stm->bindParam(':id', $id);
$stm->execute();
$res = $stm->fetch();
foreach ($res as $key => $value) {
$this->$key = $value;
}
}
function validate ($input) {
// set properties with input
foreach ($input as $key => $value) {
$this->$key = $value;
}
// validate form input
$this->validator->set_rule('username', 'Username', $this->username, ['required']);
$this->validator->set_rule('email_address', 'Email Address', $this->email_address, ['required', 'email_address']);
// return boolean result of validation attempt
return $this->validator->validate();
}
function update ($id) {
// update user by id
$stm = $this->pdo->prepare('update `user` set `username`=:username, `email_address`=:email_address where `id`=:id');
$stm->bindParam(':username', $this->username);
$stm->bindParam(':email_address', $this->email_address);
$stm->bindParam(':id', $id);
$stm->execute();
}
}
I know that I should probably use an ORM or something, but I really prefer just using PDO queries directly. Also, should I be creating a method for setting the class properties via an array within the model
class?
Looking at this code, am I doing everything correctly? I know I didn't show the view because it isn't coded yet, but the view will have access to the sanitizer and do checks for any session messages (such as errors and successes). That is it aside from HTML output.
Also, is this actually OOP or am I just infinitely stuck in this procedural pattern I can't get out of my head?