0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PHPで「お問い合わせ画面」を実装してみた

Last updated at Posted at 2022-06-12
../

PHPの練習のため、お問い合わせの画面を実装してみた。画面遷移は以下の感じ。
contact-states.png
オブジェクト指向でモデルを実装する。クラス図は以下の感じ。
contact-classes.png

問い合わせのエンティティ(Contact)は、とりあえずシンプルに名前、メールアドレス、問い合わせの内容の3属性とした。setter/getterは割愛。今回は永続化していないので、エンティティは単に編集フォームとして利用しているだけ。

Contact.php
<?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は以下の感じ。

ContactDataAccessor.php
<?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()は、[確認]時にも、[送信]時にも呼ばれるようにしておく。

ContactManager.php
<?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に保持するコンテキストはできるだけまとめておく方針。

ContactContext.php
<?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から。

index.php
<?php namespace contact; ?>
<a href="contact-input.php">お問い合わせ</a>

次に、お問い合わせ入力画面。

contact-input.php
<?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(); ?>

次に、お問い合わせ確認画面。

contact-confirm.php
<?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>

次に、お問い合わせ完了画面。

contact-complete.php
<?php namespace contact; ?>
<div>
  <p>お問い合わせをいただき、ありがとうございます。<br/>
  数日内に回答させていただきます</p>
  <p><br></p>
  <p><a href="index.php">トップへ</a></p>
</div>

最後に画面遷移のためのcontact-actions.phpを。[確認]ボタン、[送信]ボタン押下時のアクションを記述している。[リセット]でも利用してみた。

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);
?>

以上

../
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?