Wordpress Contact Form 7 で問い合わせ種別に応じて複数の宛先に管理者向けメールを送る方法

# はじめに

問い合わせ種別に応じて、各担当のメールアドレス宛にメールを送りたいこと、あると思います。
Contact Form 7は以下のようにプルダウンのvalueにメールアドレスを設定すれば、やりたいことが出来るには出来るのですが…

```php
[select* type id:type class:form-select include_blank "イベントに関する問い合わせ|test+event@example.co.jp" "求人に関する問い合わせ|test+recruit@example.co.jp" "ホームページに関する問い合わせ|test+web@example.co.jp" "その他|test+etc@example.co.jp"]
```

valueにメールアドレスの一覧を羅列するのって結構抵抗ありませんか？ 私はあります。

担当のメールアドレスが変わったら、メンテナンスが大変ですし、
担当のメールアドレスが複数あったら、実装も大変になります。

なので、Wordpress管理画面にオプション画面を設置し、
問い合わせ種別に応じて、それぞれ複数のメールアドレス宛にメールが送れるようにします。

![スクリーンショット 2024-09-03 163115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/e9104b50-d0b8-5c26-2f51-8ac8e910dd11.png)


オプション画面を初めて作ったので、もっと良い方法や、これを実現できる既製プラグインがあれば、ご紹介をお願いします。

# 実装

## お問い合わせフォームテンプレート

### お問い合わせ > コンタクトフォーム > フォーム

```php
<p>問い合わせ種別</p>
[select* type id:type class:form-select include_blank "イベントに関する問い合わせ|event" "求人に関する問い合わせ|recruit" "ホームページに関する問い合わせ|web" "その他|etc"]

<p>メールアドレス</p>
[email* your-email autocomplete:your-email id:your-email class:form-text class:form-email]

[submit "送信する"]
```

### お問い合わせ > コンタクトフォーム > メール > メール1(管理者向けメール)

`[_raw_{field_name}]` と記述することで、プルダウンのvalueの値を出力可能です

```php
送信先: [_site_admin_email]
送信元: [_site_title] <wordpress@example.co.jp>
題名: [_site_title] "[your-subject]"
追加ヘッダー: Reply-To: [your-email]
メッセージ本文:
お問い合わせの種類・目的: [_raw_type]
メールアドレス: [your-email]
```

### お問い合わせ > コンタクトフォーム > メール > メール2(ユーザ向けメール)

```php
送信先: [your-email]
送信元: [_site_title] <wordpress@example.co.jp>
題名: [_site_title] "[your-subject]"
追加ヘッダー: Reply-To: [_site_admin_email]
メッセージ本文:
お問い合わせの種類・目的: [_raw_type]
メールアドレス: [your-email]
```

## ロジック

### 共通関数ファイル `functions.php`

```php:wp-content/themes/{theme_name}/functions.php
/*
 * 管理画面に送信先管理のオプション画面を追加する
 */
add_action('admin_menu', 'cf7_custom_inquiry_option_menu');

function cf7_custom_inquiry_option_menu()
{
    add_menu_page(
        'お問い合わせ送信先管理', // ページタイトル
        'お問い合わせ送信先管理', // メニュータイトル
        'manage_options', // 権限
        'cf7-custom-inquiry-option', // ページスラッグ
        'cf7_custom_inquiry_option_page' // コールバック関数 includes/cf7_custom_inquiry_option.php に定義
    );
	add_action('admin_init', 'cf7_custom_inquiry_register_settings'); // includes/cf7_custom_inquiry_option.php に定義
}

// メール送信ロジックやオプション画面は以下のファイルに記載
require ( dirname(__FILE__) . '/includes/cf7_custom_inquiry_option.php');
```

### オプション画面 `includes/cf7_custom_inquiry_option.php`

```php:wp-content/themes/{theme_name}/includes/cf7_custom_inquiry_option.php
<?php
/**
 * [管理画面] お問い合わせ送信先管理
 * お問い合わせ種別
 */
function cf7_custom_inquiry_get_inquiry_types() {
    $inquiryCodes = [
        'default' => "(デフォルト)その他",
        'event' => "イベントに関する問い合わせ",
        'recruit' => "求人に関する問い合わせ",
        'web' => "ホームページに関する問い合わせ"
    ];
    return $inquiryCodes;
}

/**
 * [管理画面] お問い合わせ送信先管理
 * オプションページを表示する
 */
function cf7_custom_inquiry_option_page() {
    $inquiryCodes = cf7_custom_inquiry_get_inquiry_types();

    // DBから送信先別メールアドレス一覧を取得
    $storedEmails = get_option('cf7_custom_inquiry_emails');

    // DB上にデータが存在しない時にエラーが出るのを抑制
    if (! $storedEmails || ! is_array($storedEmails)) {
        $storedEmails = [];
    }

    /* ★オプションページ描画 ここから★ */
?>
    <div class="wrap">
        <h2>お問い合わせ送信先管理</h2>

        <?php settings_errors('cf7_custom_inquiry_emails'); ?>

        <form method="post" action="options.php">
            <?php settings_fields('cf7_custom_inquiry_settings_group'); ?>
            <h3>送信先メールアドレスを改行区切りで一つずつ入力してください</h3>
            <p>デバッグ用</p>
            <pre><?= var_dump($storedEmails); ?></pre>
            <pre><?= var_dump(get_mail_address_lists_of_inquiry()); ?></pre>
            <table class="form-table">
                <?php foreach ($inquiryCodes as $code => $label): ?>
                    <tr valign="top">
                        <th scope="row"><?= $label; ?></th>
                        <td>
                            <textarea name="cf7_custom_inquiry_emails[<?= $code; ?>]" rows="3" cols="50" required><?= esc_textarea(isset($storedEmails[$code]) ? $storedEmails[$code] : ''); ?></textarea>
                        </td>
                    </tr>
                <?php endforeach; ?>
            </table>
            <?php submit_button(); ?>
        </form>
    </div>
<?php
    /* ★オプションページ描画 ここまで★ */
}

/**
 * [管理画面] お問い合わせ送信先管理
 * 設定グループをWordPressに登録
 */
function cf7_custom_inquiry_register_settings() {
    register_setting('cf7_custom_inquiry_settings_group', 'cf7_custom_inquiry_emails', 'cf7_custom_inquiry_validate');
}

/**
 * [管理画面] お問い合わせ送信先管理
 * カスタムバリデーションルール追加
 */
function cf7_custom_inquiry_validate($input) {
    $inquiryCodes = cf7_custom_inquiry_get_inquiry_types();
    $errors = [];
    $validInput = $input; // 元の入力内容を保持

    foreach ($inquiryCodes as $code => $label) {
        $emails = explode("\n", trim($input[$code])); // 改行区切りでメールアドレスを展開
        $emails = array_map('trim', $emails);

        // 必須入力チェック
        if (empty($emails)) {
            add_settings_error(
                'cf7_custom_inquiry_emails',
                'cf7_custom_inquiry_emails_error_' . $code,
                sprintf('%s は必須入力です。', $label),
                'error'
            );
            $errors[] = $code;

            continue;
        }

        // メールアドレス妥当性、重複チェック
        $uniqueEmails = [];
        foreach ($emails as $email) {
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                add_settings_error(
                    'cf7_custom_inquiry_emails',
                    'cf7_custom_inquiry_emails_format_error_' . $code,
                    sprintf('%s に無効なメールアドレスが含まれています: %s', $label, $email),
                    'error'
                );
                $errors[] = $code;
            }
            elseif (in_array($email, $uniqueEmails)) {
                add_settings_error(
                    'cf7_custom_inquiry_emails',
                    'cf7_custom_inquiry_emails_duplicate_error_' . $code,
                    sprintf('%s に重複するメールアドレスが含まれています: %s', $label, $email),
                    'error'
                );
                $errors[] = $code;
            }
            else {
                $uniqueEmails[] = $email; // 登録済メールアドレスとして格納
            }
        }

        // 入力に問題がない場合、送信先別メールアドレスリストに登録
        if (! empty($uniqueEmails)) {
            $validInput[$code] = implode("\n", $uniqueEmails);
        }
    }

    // エラーがあれば入力値をそのまま返すが、保存はしないようにする(pre_update filterで制御)
    if (! empty($errors)) {
        return $input;
    }

    // エラーがない場合に成功メッセージを追加
    add_settings_error(
        'cf7_custom_inquiry_emails',
        'cf7_custom_inquiry_emails_success',
        'お問い合わせ送信先を保存しました。',
        'success'
    );

    // エラーがなければバリデーション通過した内容を返す
    return $validInput;
}

/**
 * 保存前のオプションの値をフィルタリングして、エラーがあれば保存を中止する。
 */
function cf7_custom_inquiry_pre_update($new_value, $old_value) {
    // ここでバリデーションエラーがあった場合に旧値をそのまま返し、新しい値を保存しない
    $errors = get_settings_errors('cf7_custom_inquiry_emails');
    if (!empty($errors)) {
        // エラーがある場合は古い値を返す
        return $old_value;
    }
    // エラーがない場合は新しい値を返す
    return $new_value;
}

add_filter('pre_update_option_cf7_custom_inquiry_emails', 'cf7_custom_inquiry_pre_update', 10, 2);

/**
 * [管理画面／フロント]
 * お問い合わせ種別ごとのメール飛ばし先リストを取得する
 */
function get_mail_address_lists_of_inquiry()
{
    $defaultMailAddresses = [
        'default' => ['test@example.co.jp', 'test+etc@example.co.jp'], // その他・デフォルト
        'event' => ['test@example.co.jp', 'test+event@example.co.jp'], // イベント
        'recruit' => ['test@example.co.jp', 'test+recruit@example.co.jp'], // 求人
        'web' => ['test@example.co.jp', 'test+web@example.co.jp'], // ホームページ
    ];

    // 取得したメールアドレスの配列
    $storedEmails = get_option('cf7_custom_inquiry_emails');

    // DB上にデータが存在しない場合、デフォルトのメールアドレスリストを使用
    if (!$storedEmails || !is_array($storedEmails)) {
        $storedEmails = $defaultMailAddresses;
        return $storedEmails;
    }

    // 新しいメールアドレス配列を作成
    $mailAddresses = [];

    // インデックスを設定
    $index = 0;

    // 配列のキーと値をループ処理
    foreach ($storedEmails as $code => $emails) {
        // 改行で分割してメールアドレスの配列を取得
        $emailList = array_map('trim', explode("\n", $emails));
        // インデックスに基づいてメールアドレスにサフィックスを追加
        foreach ($emailList as $email) {
            // メールアドレスにサフィックスを追加
            $mailAddresses[$code][] = $email;
            $index++;
        }
    }

    return $mailAddresses;
}

/**
 * [フロント] お問い合わせフォーム／採用応募フォーム
 * お問い合わせ種別によってメールの飛ばし先を変更する
 * 確認画面後、送信するボタンを押下時に発火する
 * 管理者向けメール(メール1)、ユーザ向けメール(メール2)の2回、発火する
 */
function custom_wpcf7_mail_components($components, $form, $args)
{
    $targetKey = 'type';

    // デフォルトの送り先
    $toAddressOrg = $components['recipient'];

    // 管理者メールアドレスを取得
    $adminEmail = get_option('admin_email');

    $sendType = ($toAddressOrg === $adminEmail) ? "mail_1" : "mail_2";
    // ユーザー向けメール(mail_2)の送信なら以降の処理を中断
    // WARNING: このロジックだと、管理者ユーザーが自分に対してメールを送ると、処理が継続する
    if($isSendToUser = $sendType === "mail_2") {
        $components['recipient'] = $toAddressOrg; // mail_1の処理で送り先が書き換わるので元の送り先に戻す
        return $components;
    }

    // 管理者向けメール(mail_1)のみ、送り先を変更する
    if ($submission = WPCF7_Submission::get_instance()) {
        $postedData = $submission->get_posted_data();

        // NOTE: プルダウンの値(typeやprefectureなど)は配列型で値が来る模様
        $isValueExists = false;
        $value = "";

        if(isset($postedData[$targetKey])){
            $isPulldownField = (is_array($postedData[$targetKey]) && count($postedData[$targetKey]) > 0);
            $isHiddenField = (is_string($postedData[$targetKey]) && strlen($postedData[$targetKey]) > 0);

            if($isPulldownField) $value = $postedData[$targetKey][0];
            if($isHiddenField) $value = $postedData[$targetKey];
        }

        // お問い合わせ種別ごとのメールアドレス配列を定義
        $toAddresses = get_mail_address_lists_of_inquiry();
        // 管理者のメールアドレスを設定
        $components['recipient'] = implode(', ', $toAddresses["default"]);

        if(array_key_exists($value, $toAddresses)){
            $components['recipient'] = implode(', ', $toAddresses[$value]);
        }
    }
    return $components;
}

add_filter('wpcf7_mail_components', 'custom_wpcf7_mail_components', 10, 3);
```

## 動作確認

オプション画面でメールアドレスを設定後、問い合わせフォームを送信すると、問い合わせ種別に応じて、管理者メールのみ、対応するメールアドレス一覧にメールを送ります。
オプション画面でメールアドレスを未設定の場合でも、デフォルトの送信先メールアドレス一覧をセットします。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/0e0816ce-23e4-fd7b-6df1-5e6e82556852.png)

注意点として、メールアドレスの送信先が管理者メールアドレスと同じ場合、管理者メールもユーザー向けメールも送信先がオプション画面で設定したメールアドレス一覧になります。メール1とメール2のイイ感じの識別方法がわからなかったため。