0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

WordPress 發表新文章自動寄信通知會員:正確寫法與大量寄信的注意事項

地球與新聞背景

經營開放註冊的會員網站,很多人會希望:網站一發表新文章,就自動寄 Email 通知會員回來看。

這個功能用程式碼做得到,但「寄信」這件事比想像中麻煩:寄太多會被主機擋、會進垃圾信匣,寫法不對還會在每次修改文章時重複寄給所有人。這篇先講清楚該用什麼方式,再提供正確的程式碼。

先說結論:會員多,就用電子報服務

  • 會員不多(例如幾十人),想自己做:可以用下面的程式碼,搭配 SMTP 外掛寄信。
  • 會員上百人以上,或想看開信率、做排版漂亮的信:直接用電子報服務或外掛,例如 MailPoet、Mailchimp、Brevo,或 Jetpack 的訂閱功能。它們大多有「發佈新文章時自動寄送」的功能,退訂、退信、寄送速度都幫你處理好。

為什麼不要在發文時一次寄給所有會員?

  • 發佈會卡住:如果在按下「發佈」的當下一封一封寄,要等所有信寄完頁面才會回應。會員一多就可能逾時,編輯器甚至顯示發佈失敗。
  • 主機有寄信上限:大部分主機都有每小時可以寄出的信件數限制,一次寄太多會被擋,嚴重的話整個網站都寄不出信(包含會員註冊、忘記密碼的信)。
  • 容易進垃圾信匣:WordPress 預設用主機的 PHP 寄信,沒有經過正式的寄信驗證,很容易被當成垃圾信。

所以不管用哪種方式,都建議先裝 SMTP 外掛(例如 WP Mail SMTP、FluentSMTP),透過正式的郵件服務寄信。這類外掛大多有「寄送測試信」的功能,設定完先測試一次。SMTP 的設定概念可以參考用 Gmail 收發公司企業信箱的寄件(SMTP)設定。

舊版程式碼的問題

這篇文章舊版的程式碼,現在使用會有幾個問題:

  • 用 publish_post 觸發:這個 hook 不只在第一次發佈時執行,已發佈的文章每次更新也會執行。改一個錯字,全部會員就再收到一封信。
  • 用 $_POST['original_post_status'] 判斷:區塊編輯器不會送出這個欄位,判斷會失效,PHP 8 還會出現警告。
  • 寄給資料庫裡所有帳號:包含管理員、沒打算收信的會員,也沒有提供退訂的方法。
  • 在發佈當下同步寄出:就是上一節說的問題。

正確寫法:只寄給願意收信的會員,分批在背景寄送

下面的程式碼分成三個部分:

  1. 會員在個人資料頁自己勾選「有新文章時寄信通知我」。
  2. 文章第一次發佈時,不馬上寄信,而是排一個背景工作。
  3. 背景工作每次寄 20 封,寄完再排下一批,直到寄完。

程式碼加在子主題的 functions.php,或用 Code Snippets 外掛新增(說明請看 WordPress functions.php 實用程式碼整理)。

第一部分:會員自己選擇要不要收信

PHP
// 個人資料頁加上「新文章通知」勾選
add_action( 'show_user_profile', 'ahwei_notify_field' );
add_action( 'edit_user_profile', 'ahwei_notify_field' );
function ahwei_notify_field( $user ) {
    ?>
    <h2>新文章通知</h2>
    <table class="form-table" role="presentation">
        <tr>
            <th scope="row">Email 通知</th>
            <td>
                <label>
                    <input type="checkbox" name="ahwei_notify" value="1" <?php checked( get_user_meta( $user->ID, 'ahwei_notify', true ), '1' ); ?>>
                    網站有新文章時寄信通知我
                </label>
            </td>
        </tr>
    </table>
    <?php
}

// 儲存勾選結果
add_action( 'personal_options_update', 'ahwei_save_notify_field' );
add_action( 'edit_user_profile_update', 'ahwei_save_notify_field' );
function ahwei_save_notify_field( $user_id ) {
    if ( current_user_can( 'edit_user', $user_id ) ) {
        update_user_meta( $user_id, 'ahwei_notify', isset( $_POST['ahwei_notify'] ) ? '1' : '0' );
    }
}

WordPress 在儲存個人資料之前,已經做過表單的安全檢查,這裡再確認目前的使用者有權限修改這個帳號。

第二、三部分:第一次發佈時排入背景,分批寄送

PHP
// 文章「第一次」發佈時,排入背景工作(不在發佈當下寄信)
add_action( 'transition_post_status', function ( $new_status, $old_status, $post ) {
    if ( 'publish' !== $new_status || 'publish' === $old_status || 'post' !== $post->post_type ) {
        return;
    }
    if ( get_post_meta( $post->ID, '_ahwei_notified', true ) ) {
        return; // 通知過了(例如先取消發佈、又重新發佈)
    }
    update_post_meta( $post->ID, '_ahwei_notified', 1 );
    wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'ahwei_send_post_notice', array( $post->ID, 0 ) );
}, 10, 3 );

// 背景工作:每批寄 20 封,寄完再排下一批
add_action( 'ahwei_send_post_notice', function ( $post_id, $offset ) {
    $post = get_post( $post_id );
    if ( ! $post || 'publish' !== $post->post_status ) {
        return;
    }

    $batch = 20;
    $users = get_users( array(
        'meta_key'   => 'ahwei_notify',
        'meta_value' => '1',
        'fields'     => array( 'ID', 'user_email' ),
        'orderby'    => 'ID',
        'order'      => 'ASC',
        'number'     => $batch,
        'offset'     => $offset,
    ) );

    $site    = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
    $title   = wp_strip_all_tags( $post->post_title );
    $subject = sprintf( '【%s】新文章:%s', $site, $title );
    $message = sprintf(
        "%s 發表了新文章:\n\n%s\n%s\n\n不想再收到通知,請到個人資料頁取消勾選:\n%s",
        $site,
        $title,
        get_permalink( $post ),
        admin_url( 'profile.php' )
    );

    foreach ( $users as $user ) {
        wp_mail( $user->user_email, $subject, $message );
    }

    // 這批寄滿了,代表可能還有下一批
    if ( count( $users ) === $batch ) {
        wp_schedule_single_event(
            time() + 5 * MINUTE_IN_SECONDS,
            'ahwei_send_post_notice',
            array( $post_id, $offset + $batch )
        );
    }
}, 10, 2 );

這樣寫的好處:

  • 只在第一次發佈時寄:用 transition_post_status 判斷「從不是發佈變成發佈」,之後更新文章不會再寄;排程發佈的文章,到時間發佈時也會正常通知。
  • 不會重複寄:寄過的文章會記一個 _ahwei_notified 標記,就算先取消發佈再重新發佈,也不會再寄一次。
  • 發佈不會卡住:寄信交給背景工作,按下發佈馬上完成。
  • 分批寄送:每 5 分鐘寄 20 封,一小時大約 240 封。請依照主機或 SMTP 服務的寄送上限,調整 $batch 和間隔時間。
  • 信裡附上退訂方式:會員隨時可以到個人資料頁取消勾選。

使用前要知道的事

  • 背景工作靠 WP-Cron 執行:WordPress 的排程是在有人瀏覽網站時才觸發,流量很少的網站,信可能會晚一點寄出。可以請主機商設定系統排程,定時執行網站的 wp-cron.php,時間會比較準。
  • 會員要進得了個人資料頁:如果你用了禁止非管理者進入後台的做法,會員就打不開個人資料頁,勾選功能要改放在前台的會員頁面,或直接改用電子報外掛。
  • 先測試再上線:先用自己的測試帳號勾選通知,發一篇測試文章,確認信件內容、寄件者名稱都正確,也沒有進垃圾信匣。

延伸閱讀

會員經營要長久,信要寄得到、也不能打擾人。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與網站維護託管,服務台北與全台客戶,歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!