Revision 93ed1f08-dbb2-4fb5-a42e-ffa8c99d43d8 - Code Review Stack Exchange
I know the Model, Controller and View's purpose, but I have never really found a good concrete class example of a View class.
Usually I see people having some small `render()` method being called from the Controller, or their Controller holds all the View logic. So I tried my best and tried to create a View class that seemed proper and somewhat clean to me.
I've found a useful [image][1] that shows how the data flow is supposed to be and how the View sits in it.
- **Is this a valid View class?**
- **Any points I could improve on (suggestions)?**
Readability, usability, efficiency.
---
**LoginView.php**
namespace View;
use View\View;
use Http\HttpResponse;
class LoginView extends View
{
private $templatePath;
private $templateData;
private $isUserLoggedIn;
private $isTokenValid;
public function __construct(HttpResponse $httpResponse)
{
parent::__construct($httpResponse);
$this->templatePath = ABSPATH . DS . 'View' . DS . 'Template' . DS . 'Login' . DS . 'Index.php';
$this->templateData['title'] = 'Log In';
$this->templateData['styleSheets'] = ['/stylesheets/login.css'];
$this->templateData['javaScripts'] = [
'//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js',
'/javascripts/login-form-handler.min.js'
];
}
public function index()
{
if ($this->isUserLoggedIn) {
$this->httpResponse->redirect('/');
}
if ($this->isTokenValid === false) {
$this->httpResponse->redirect();
}
$formErrorMessages = [
'email' => [
'NotEmpty' => 'Enter your email address.',
'Email' => 'You did not enter a valid email address.'
],
'password' => [
'NotEmpty' => 'Enter your password.'
]
];
if (isset($this->templateData['form']['errors'])) {
$this->templateData['form']['errors'] = $this->replaceArrayKeys($this->templateData['form']['errors'], $formErrorMessages);
}
$this->renderTemplate($this->templatePath, $this->templateData);
}
public function setIsUserLoggedIn($boolean)
{
$this->isUserLoggedIn = $boolean;
}
public function setIsTokenValid($boolean)
{
$this->isTokenValid = $boolean;
}
public function setFormValueOf($name, $value)
{
$this->templateData['form']['values'][$name] = $value;
}
public function setFormErrorCodeOf($name, $code)
{
$this->templateData['form']['errors'][$name] = $code;
}
public function setPersistentUserLoginCookie($name, $userId, $seriesNumber, $token)
{
$this->httpResponse->makeCookie($name, [
'uid' => $userId,
'sno' => $seriesNumber,
'token' => $token
], strtotime('+90 days'));
}
public function setHasLoginFailed($boolean)
{
$this->templateData['hasLoginFailed'] = $boolean;
}
}
Parent **View.php**
<?php
namespace View;
use \Http\HttpResponse;
abstract class View
{
protected $httpResponse;
public function __construct(HttpResponse $httpResponse)
{
$this->httpResponse = $httpResponse;
}
public function replaceArrayKeys(array $array, array $source)
{
foreach ($array as $k => $v) {
for ($i = 0, $c = count($v); $i < $c; ++$i) {
if (isset($source[$k][$v[$i]])) {
$array[$k][$i] = $source[$k][$v[$i]];
}
}
}
return $array;
}
public function renderTemplate($path, $data)
{
extract($data);
include_once ABSPATH . DS . 'View' . DS . 'Template' . DS . 'Header.php';
ob_start([$this, 'ob_indent']);
require_once $path;
ob_end_flush();
include_once ABSPATH . DS . 'View' . DS . 'Template' . DS . 'Footer.php';
}
private function ob_indent($buffer)
{
$content = '';
$lines = explode(PHP_EOL, $buffer);
foreach ($lines as $line) {
$content .= str_repeat(' ', 12) . $line . PHP_EOL;
}
return $content;
}
}
**LoginController.php** (for clarification)
namespace Controller;
use View\LoginView;
use Http\HttpRequest;
use Controller\Controller;
use Model\Application\AntiCsrfService;
use Model\Application\AuthenticationService;
use Model\Application\FormValidationService;
class LoginController extends Controller
{
private $antiCsrfService;
private $formValidationService;
public function __construct(
HttpRequest $httpRequest,
AuthenticationService $authenticationService,
LoginView $loginView,
AntiCsrfService $antiCsrfService,
FormValidationService $formValidationService
) {
parent::__construct($httpRequest, $authenticationService, $loginView);
$this->antiCsrfService = $antiCsrfService;
$this->formValidationService = $formValidationService;
if ($this->authenticationService->findLoggedInUser()) {
// User has no reason to be on this page,
// so it will redirect to somewhere.
$this->view->setIsUserLoggedIn(true);
$this->view->index();
}
}
public function index()
{
$this->view->setFormValueOf('csrfToken', $this->antiCsrfService->getToken());
$this->view->setFormValueOf('remember', true);
$this->view->index();
}
public function submit()
{
$token = $this->httpRequest->findParameter($this->antiCsrfService->getToken()['name']);
if ($this->antiCsrfService->isTokenValid($token)) {
$isLoginFormValid = $this->formValidationService->isLoginValid(
trim($this->httpRequest->findParameter('email')),
$this->httpRequest->findParameter('password')
);
if ($isLoginFormValid) {
$isLoginSuccessful = $this->authenticationService->loginUser(
trim($this->httpRequest->findParameter('email')),
$this->httpRequest->findParameter('password'),
$this->httpRequest->findParameter('remember') ? true : false
);
}
if (!$isLoginFormValid || !$isLoginSuccessful) {
$this->view->setHasLoginFailed($isLoginFormValid ? true : false);
$this->view->setFormValueOf('csrfToken', $this->antiCsrfService->getToken());
$this->view->setFormValueOf('email', $this->httpRequest->findParameter('email'));
$this->view->setFormValueOf('remember', $this->httpRequest->findParameter('remember', false));
foreach ($this->formValidationService->findErrorsOf('Login') as $key => $value) {
$this->view->setFormErrorCodeOf($key, $value);
}
$this->view->index();
return null;
}
// TODO let view create persistent user login cookie if opted
$this->view->setIsUserLoggedIn(true);
$this->antiCsrfService->regenerateToken();
} else {
$this->view->setIsTokenValid(false);
}
$this->view->index();
return null;
}
}
[1]: https://i.sstatic.net/GeL0I.jpg