Vue.jsとPHPで作るお問い合わせフォーム

お問い合わせフォームは、多くのウェブサイトで必要とされる重要な機能です。本記事では、Vue.jsを使って動的なユーザーインターフェイスを作り、PHPを使って問い合わせ内容を処理・送信するシンプルなフォームを構築します。


使用したツールとバージョン情報

  • Vue.js: 3.x (CDN経由で最新バージョンを使用 ※テストは 3.5.13)
  • Tailwind CSS: 3.x (CDN経由で最新バージョンを使用 ※テストは 3.4.15)
  • PHP: 8.2.22
  • エックスサーバー

完成イメージ

このお問い合わせフォームは以下の3ステップで動作します:

  1. 入力画面:ユーザーが名前、メールアドレス、お問い合わせ内容を入力。
  2. 確認画面:入力内容を確認し、修正または送信が可能。
  3. 完了画面:送信完了メッセージを表示。

さらに、以下の機能を実装しています:

  • 動的な画面遷移: Vue.jsでステップごとに画面を切り替え。
  • 自動返信メール: 問い合わせ者に確認メールを送信。
  • レスポンシブデザイン: Tailwind CSS

必要な技術

  • Vue.js: データバインディングとリアクティブな画面更新。
  • PHP: メール送信や問い合わせ内容の処理。
  • Tailwind CSS: デザインの適用。

コードの全体像

1. フロントエンド (index.html)

以下のHTMLコードでは、Vue.jsを利用して画面を動的に切り替え、Tailwind CSSでデザインを整えています。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>お問い合わせフォーム</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <script src="https://unpkg.com/vue@3"></script>
</head>

<body class="bg-gray-100 min-h-screen flex items-center justify-center">
  <div id="app" class="bg-white shadow-md rounded p-6 w-full max-w-md">
    <!-- 入力ページ -->
    <div v-if="currentStep === 'input'">
      <h1 class="text-2xl font-bold mb-4">お問い合わせフォーム</h1>
      <form @submit.prevent="goToConfirm">
        <div class="mb-4">
          <label for="name" class="block text-sm font-medium text-gray-700">名前:</label>
          <input type="text" v-model="formData.name" id="name" required
            class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
        </div>
        <div class="mb-4">
          <label for="email" class="block text-sm font-medium text-gray-700">メールアドレス:</label>
          <input type="email" v-model="formData.email" id="email" required
            class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
        </div>
        <div class="mb-4">
          <label for="message" class="block text-sm font-medium text-gray-700">お問い合わせ内容:</label>
          <textarea v-model="formData.message" id="message" required
            class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"></textarea>
        </div>
        <button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">
          確認画面へ
        </button>
      </form>
    </div>

    <!-- 確認ページ -->
    <div v-if="currentStep === 'confirm'">
      <h1 class="text-2xl font-bold mb-4">内容の確認</h1>
      <p class="mb-2"><strong>名前:</strong> {{ formData.name }}</p>
      <p class="mb-2"><strong>メールアドレス:</strong> {{ formData.email }}</p>
      <p class="mb-4"><strong>お問い合わせ内容:</strong> {{ formData.message }}</p>
      <div class="flex space-x-4">
        <button @click="currentStep = 'input'" class="w-1/2 bg-gray-600 text-white py-2 px-4 rounded hover:bg-gray-700">
          戻る
        </button>
        <button @click="submitForm" class="w-1/2 bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">
          送信する
        </button>
      </div>
    </div>

    <!-- 完了ページ -->
    <div v-if="currentStep === 'complete'">
      <h1 class="text-2xl font-bold mb-4">送信が完了しました</h1>
      <p>お問い合わせいただきありがとうございます。</p>
      <button @click="resetForm" class="mt-4 w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">
        フォームに戻る
      </button>
    </div>
  </div>

  <script>
    const { createApp } = Vue;

    createApp({
      data() {
        return {
          currentStep: 'input', // 現在のステップ ('input', 'confirm', 'complete')
          formData: {
            name: '',
            email: '',
            message: ''
          }
        };
      },
      methods: {
        resetForm() {
          this.formData = {
            name: '',
            email: '',
            message: ''
          };
          this.currentStep = 'input';
        },
        goToConfirm() {
          this.currentStep = 'confirm';
        },
        async submitForm() {
          try {
            const response = await fetch('backend/contact.php', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(this.formData)
            });
            const data = await response.json();
            if (data.success) {
              this.currentStep = 'complete';
            } else {
              alert('送信に失敗しました: ' + (data.error || '不明なエラー'));
              this.currentStep = 'input';
            }
          } catch (error) {
            alert('エラーが発生しました。');
            this.currentStep = 'input';
          }
        }
      }
    }).mount('#app');
  </script>
</body>

</html>

2. バックエンド (contact.php)

以下のPHPコードは、問い合わせ内容を処理し、管理者への通知と自動返信メールを送信します。

<?php
header('Content-Type: application/json');

$data = json_decode(file_get_contents('php://input'), true);

if (empty($data['name']) || empty($data['email']) || empty($data['message'])) {
    echo json_encode(['success' => false, 'error' => '入力内容が不完全です。']);
    exit;
}

// 管理者への通知メール
$toAdmin = 'admin@example.com'; // 仮の送信先アドレス
$subjectAdmin = 'お問い合わせフォームからのメッセージ';
$messageAdmin = "名前: {$data['name']}\nメールアドレス: {$data['email']}\nメッセージ: {$data['message']}";
$headersAdmin = 'From: noreply@example.com'; // 仮のFromアドレス

$mailToAdmin = mail($toAdmin, $subjectAdmin, $messageAdmin, $headersAdmin);

// 問い合わせ者への自動返信メール
$toUser = $data['email'];
$subjectUser = 'お問い合わせありがとうございます';
$messageUser = "{$data['name']} 様\n\nお問い合わせありがとうございます。\n以下の内容で受け付けました。\n----------------------\nお名前: {$data['name']}\nメールアドレス: {$data['email']}\nお問い合わせ内容:\n{$data['message']}\n----------------------\n\n担当者より折り返しご連絡いたします。";
$headersUser = 'From: noreply@example.com'; // 仮のFromアドレス

$mailToUser = mail($toUser, $subjectUser, $messageUser, $headersUser);

if ($mailToAdmin && $mailToUser) {
    echo json_encode(['success' => true]);
} else {
    echo json_encode(['success' => false, 'error' => 'メール送信に失敗しました。']);
}

Vue.jsの動きの説明

ステップ遷移

currentStepというデータを利用して、画面の状態を管理します。v-ifディレクティブで以下を切り替えます:

  • input: 入力画面
  • confirm: 確認画面
  • complete: 完了画面

データバインディング

formDataオブジェクトをv-modelで各入力フィールドとバインド。これにより、フォームの

入力値がリアルタイムで更新されます。

非同期通信

submitFormメソッドでfetchを使用してPHPスクリプトにデータを送信し、サーバーのレスポンスを処理します。


デモスクリーンショット

入力画面

確認画面

送信完了画面

コメント

タイトルとURLをコピーしました