And to take this example further still, let’s take the same idea and extend it so that the user can select a city from a <select>
and it will display the time in that city.
Using Web MVC:
clock.html.php
<form action="/clock" method="post">
<label>Select a city</label>
<select name="timezone" onchange="this.form.submit();">
<?php foreach ($timezones as $timezone => $cityName) {
?>
<option value="<?= $timezone; ?>" <?= (isset($city) && $city === $cityName ? 'selected="selected"' : ''); ?><?= $city; ?></option>
<? } ?>
</select>
</form>
<?php
if (isset($city)) {
?>
The time in <em><?= $city; ?></em> is <strong><?= $time; ?></strong>
<?php
}
?>
clockmodel.php
class ClockModel implements FormModel {
public $timezones = ['Europe/London' => 'London',
'America/New_York' => 'New York',
'America/Los_Angeles', 'Los Angeles'];
public function getTime($timezoneStr) {
$timezone = new DateTimeZone($timezone);
$date = new DateTime;
$date->setTimeZone($timezone);
return $date;
}
public function isValid($timezone) {
return isset($this->timezones[$timezone]);
}
public function getCity($timezone) {
return $this->timezones[$timezone];
}
}
clockcontroller.php
class ClockController {
private $template;
public function __construct(Template $template) {
$this->template = $template;
}
public function viewAction() {
$model = new ClockModel;
return ['headers' => '', 'body' => $this->template->render('clock.html.php', ['timezones' => $model->timezones]);
}
public function submitAction($post) {
$model = new ClockModel;
if ($model->isValid($post)) {
return ['headers' => '', 'body' => $this->template->render('clock.html.php',
['timezones' => $model->timezones,
'city' => $model->getCity($post['timezone']),
'time' => $model->getTime($get['timezone'])]);
}
else return $this->viewAction();
}
}
index.php
else if ($_SERVER['REQUEST_URI'] === '/clock' && $_SERVER['REQUEST_METHOD'] === 'GET') {
$controller = new ClockController(new Template);
$response = $controller->viewAction();
}
else if ($_SERVER['REQUEST_URI'] === '/clock' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$controller = new ClockController(new Template);
$response = $controller->submitAction($_POST);
}
By Comparison, in TomVC
I’m going to assume the framework provides this interface and class, they’re basically the ones I presented earlier but previously used Jeff’s less generic function names.
interface FormModel {
public function submit($data);
public function isValid($data);
public function success();
}
class FormController {
private $model;
public function __construct(FormModel $model) {
$this->model = $model;
}
public function submit($data) {
if ($this->model->isValid($data)) {
if ($this->model->submit($data)) {
$this->model->success();
}
}
}
}
So the developer would provide:
class ClockModel implements FormModel {
public $timezones = ['Europe/London' => 'London',
'America/New_York' => 'New York',
'America/Los_Angeles', 'Los Angeles'];
public $timezone;
public $submitted = false;
public function isValid($post) {
return isset($this->timezones($post['timezone']));
}
public function submit($data) {
$this->timezone = $data['timezone'];
}
public function success() {
$this->submitted = true;
}
public function getTime() {
$timezone = new DateTimeZone($this-timezone);
$date = new DateTime;
$date->setTimeZone($timezone);
return $date;
}
public function getCity() {
return $this->timezones[$this->timezone];
}
}
clock.html.php
<form action="/clock" method="post">
<label>Select a city</label>
<select name="timezone" onchange="this.form.submit();">
<?php foreach ($model->timezones as $timezone => $city) {
?>
<option value="<?= $timezone; ?>" <?= $model->timezone === $timezone ? 'selected="selected"' : ''); ?><?= $city; ?></option>
<? } ?>
</select>
</form>
<?php
if ($model-submitted) {
?>
The time in <em><?= $model->getCity(); ?></em> is <strong><?= $time; ?></strong>
<?php
}
?>
index.php
else if ($_SERVER['REQUEST_URI'] === '/clock' && $_SERVER['REQUEST_METHOD'] === 'GET') {
$model = new ClockModel;
$controller = new ClockController($model);
$view = new View('clock.html.php', $model);
}
else if ($_SERVER['REQUEST_URI'] === '/clock' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$model = new ClockModel;
$controller->submitAction($post);
$view = new View('clock.html');
}
And just to be clear, the router could be a lot smarter than this since there’s a finite number of reusable controllers:
$name = trim($_SERVER['REQUEST_URI'], '/');
if (class_exists($name . 'model')) {
$model = new ($name . 'model');
if ($model instanceof FormModel) $controller = new FormController($model);
if (file_exists($name . '.html.php')) $view = new View($name . '.html.php', $model);
}
if ($controller instanceof FormController) {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$controller->submitAction($_POST);
}
}
And only require explicit configuration when convention isn’t used.
In OOP imagine this being set up to be extensible using something like
$router->registerType('FormController', 'FormModel', function() {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$controller->submitAction($_POST);
}
});
In which case, the user wouldn’t even need to supply a route. I realise this is possible with web mvc as well but just wanted to demonstrate that it’s not exclusive to it.