../ |
---|
PHPの練習のため、お問い合わせの画面を実装してみた。画面遷移は以下の感じ。
オブジェクト指向でモデルを実装する。クラス図は以下の感じ。
問い合わせのエンティティ(Contact)は、とりあえずシンプルに名前、メールアドレス、問い合わせの内容の3属性とした。setter/getterは割愛。今回は永続化していないので、エンティティは単に編集フォームとして利用しているだけ。
<?php
namespace contact\domain;
class Contact {
public $name;
public $mail;
public $contents;
public function __construct(){
$this->init();
}
public function init(){
$this->name = '';
$this->mail = '';
$this->contents = '';
}
}
今回は永続化はしないので、スタブのみ。ContactDataAccessorは以下の感じ。
<?php
namespace contact\integration;
require_once(__DIR__."/../domain/Contact.php");
use contact\domain\Contact;
class ContactDataAccessor {
public function registerContact(Contact &$form, $validContents) : int {
// TODO スタブ
error_log($form->name . "さんからの問い合わせを処理しました。");
return 1;
}
}
ドメインロジックのContactManagerは以下の感じ。isValidContact()は、[確認]時にも、[送信]時にも呼ばれるようにしておく。
<?php
namespace contact\domain;
require_once(__DIR__."/../integration/ContactDataAccessor.php");
use contact\integration\ContactDataAccessor;
class ContactManager {
private static $instance;
private $cda;
public static function getInstance(): ContactManager {
if (empty(self::$instance)) {
self::$instance = new ContactManager();
}
return self::$instance;
}
private function __construct() {
$this->cda = new ContactDataAccessor();
}
private function isValidName(string $name) : bool {
if (empty($name) || strlen($name) > 20) return false;
return true;
}
private function isValidMail(string $mail) : bool {
if (empty($mail) || strlen($mail) > 50) return false;
$pattern = "/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/";
return preg_match($pattern, $mail);
}
private function isValidContents(string $contents) : bool {
if (empty($contents) || strlen($contents) > 2000) return false;
return true;
}
/**
* 問い合わせの入力値をチェックする。
* @param Contact $form 問い合わせフォーム
* @param array $errors エラーコンテナ
* @return bool 全ての入力値が適正ならtrue
*/
public function isValidContact(Contact &$form, array &$errors): bool {
if (!$this->isValidName($form->name)){
$errors['name'] = '※20文字以内';
}
if (!$this->isValidMail($form->mail)){
$errors['mail'] = '※50文字以内で@を含む';
}
if (!$this->isValidContents($form->contents)){
$errors['contents'] = '※2,000文字以内';
}
$b = empty($errors);
if (!$b){
$errors['_default'] = '入力値を修正ください。';
}
return $b;
}
/**
* 問い合わせ情報を登録または送信する。
* @param Contact $form 問い合わせフォーム
* @param array $errors エラーコンテナ
* @return int 処理した件数またはエラーステータス
*/
public function registerContact(Contact &$form, array &$errors) : int {
if ($this->isValidContact($form, $errors) == false){
// 入力値にエラーがある
return -1;
}
// 問い合わせ内容のみ参照表現に対応
$validContents = nl2br(htmlspecialchars($form->contents, ENT_QUOTES, 'UTF-8'));
return $this->cda->registerContact($form, $validContents);
}
}
プレゼンテーションでは、$_SESSION
に保持するコンテキストを実装しておく。Contactの編集フォームとエラーコンテナを保持する。別々に保持してもいいのだが、$_SESSION
に保持するコンテキストはできるだけまとめておく方針。
<?php
namespace contact\presentation;
require_once(__DIR__."/../domain/Contact.php");
use contact\domain\Contact;
class ContactContext {
const THIS_CONTEXT = "contact-context";
private $form;
private $errors;
public function __construct() {
$this->setForm();
$this->setErrors();
}
public static function get(): ContactContext {
if (!isset($_SESSION)){
session_start();
}
$context = null;
if (isset($_SESSION[self::THIS_CONTEXT])){
$context = $_SESSION[self::THIS_CONTEXT];
}
if ($context == null || !($context instanceof ContactContext)){
$context = new ContactContext();
$_SESSION[self::THIS_CONTEXT] = $context;
}
return $context;
}
public function getForm(): Contact {
if (empty($this->form)){
$this->form = new Contact();
}
return $this->form;
}
public function setForm(Contact $form = null): void {
$this->form = $form;
}
public function getErrors() {
return $this->errors;
}
public function setErrors(array $errors = null): void {
if ($errors == null){
$this->errors = array();
} else {
$this->errors = $errors;
}
}
public function getErrorMessage(string $key = '_default'): string {
foreach ($this->errors as $k => $v) {
if ($key == $k){
return '<font color="red">' . $v . '</font>';
}
}
return '<br>';
}
}
以下、画面を並べておく。まず、index.phpから。
<?php namespace contact; ?>
<a href="contact-input.php">お問い合わせ</a>
次に、お問い合わせ入力画面。
<?php
namespace contact;
require_once(__DIR__."/domain/Contact.php");
require_once(__DIR__."/presentation/ContactContext.php");
use contact\presentation\ContactContext;
$context = ContactContext::get();
$referer = $_SERVER['HTTP_REFERER'];
if (!strstr($referer, "contact-input.php") && !strstr($referer, "contact-confirm.php")){
$context->setForm();
$context->setErrors();
}
$form = $context->getForm();
?>
<script type="text/javascript">
function doReset(form){
form.operation.value = "reset";
form.submit();
return false;
}
</script>
<div>
<p>問い合わせの内容を入力し、[確認]してください。</p>
<p><?= $context->getErrorMessage() ?></p>
<form action="contact-actions.php" method="post">
<input type="hidden" name="operation" value="validate"/>
<div>
お名前:<br><input type="text" name="name" value="<?= $form->name ?>" placeholder="山田太郎" >
<?= $context->getErrorMessage('name') ?>
</div>
<div>
メールアドレス:<br><input type="text" name="mail" value="<?= $form->mail ?>" placeholder="yamada@xxx.com">
<?= $context->getErrorMessage('mail') ?>
</div>
<div>
問い合わせの内容:<br><textarea name="contents" rows="3" cols="30"><?= $form->contents ?></textarea>
<?= $context->getErrorMessage('contents') ?>
</div>
<div>
<br>
<input type="submit" value="キャンセル" formaction="index.php" >
<input type="submit" value="リセット" onclick="return doReset(this.form)" >
<input type="submit" value="確認" style="background-color:lightpink;" >
</div>
</form>
</div>
<?= $context->setErrors(); ?>
次に、お問い合わせ確認画面。
<?php
namespace contact;
require_once(__DIR__."/domain/Contact.php");
require_once(__DIR__."/presentation/ContactContext.php");
use contact\presentation\ContactContext;
$referer = $_SERVER['HTTP_REFERER'];
if (!strstr($referer, "contact-input.php")){
header("Location:contact-input.php");
return;
}
$context = ContactContext::get();
$form = $context->getForm();
?>
<div>
<p>問い合わせの内容を確認し、[送信]してください。</p>
<p><br></p>
<form action="contact-actions.php" method="post">
<input type="hidden" name="operation" value="register"/>
<div>
お名前:<br><input type="text" value="<?= $form->name ?>" disabled="disabled" /><br>
</div>
<div>
メールアドレス:<br><input type="text" value="<?= $form->mail ?>" disabled="disabled" /><br>
</div>
<div>
問い合わせの内容:<br/><textarea rows="3" cols="30" disabled="disabled"><?= $form->contents ?></textarea><br>
</div>
<div>
<br>
<input type="submit" value="入力に戻る" formaction="contact-input.php">
<input type="submit" value="送信" style="background-color:lightpink;" >
</div>
</form>
</div>
次に、お問い合わせ完了画面。
<?php namespace contact; ?>
<div>
<p>お問い合わせをいただき、ありがとうございます。<br/>
数日内に回答させていただきます</p>
<p><br></p>
<p><a href="index.php">トップへ</a></p>
</div>
最後に画面遷移のためのcontact-actions.phpを。[確認]ボタン、[送信]ボタン押下時のアクションを記述している。[リセット]でも利用してみた。
<?php
namespace contact;
require_once(__DIR__."/domain/ContactManager.php");
require_once(__DIR__."/presentation/ContactContext.php");
use contact\domain\ContactManager;
use contact\presentation\ContactContext;
$referer = $_SERVER['HTTP_REFERER'];
if (!strstr($referer, "contact-input.php") && !strstr($referer, "contact-confirm.php")){
header("Location:contact-input.php");
return;
}
$context = ContactContext::get();
$form = $context->getForm();
$errors = array();
$location = "contact-input.php";
$operation = $_POST['operation'];
if ($operation == 'reset'){
// [リセット]ボタン押下時
$form->init();
} else if ($operation == 'validate'){
// [確認]ボタン押下時
$form->name = $_POST['name'];
$form->mail = $_POST['mail'];
$form->contents = $_POST['contents'];
$manager = ContactManager::getInstance();
$state = $manager->isValidContact($form, $errors);
if ($state){
$location = "contact-confirm.php";
}
} else if ($operation == 'register'){
// [送信]ボタン押下時
$manager = ContactManager::getInstance();
$cnt = $manager->registerContact($form, $errors);
if ($cnt > 0){
$form->init();
$location = "contact-complete.php";
}
}
$context->setErrors($errors);
header("Location:" . $location);
?>
以上
../ |
---|