WordPress 會員中心新增追蹤文章清單功能實作

前言

這段 PHP 程式碼用於在 WordPress 會員中心(使用 MemberPress 插件)新增一個「追蹤文章」的分頁,讓使用者可以快速查看自己收藏或追蹤的文章列表。適合需要在會員系統中提供個人化內容管理的網站開發者與自學者。

新增會員中心導覽標籤

透過 mepr_account_nav action,程式碼在會員中心導覽列新增一個名為「追蹤文章」的標籤。當使用者點擊此標籤時,會帶入 action=user-follow 的 URL 參數,並且根據當前頁面狀態動態加上 CSS 樣式以標示為當前選中狀態。

add_action('mepr_account_nav', function($action){
  $active = (isset($_GET['action']) && $_GET['action'] === 'user-follow') ? 'mepr-active-nav-tab' : '';
  echo '<span class="mepr-nav-item user-follow ' . esc_attr($active) . '"><a href="/account/?action=user-follow">追蹤文章</a></span>';
});

顯示追蹤文章清單內容

使用 mepr_account_nav_content action 來判斷是否為 user-follow 頁面,若是則取得該會員收藏的文章 ID。此處利用 get_user_favorites 函式(需額外插件或自訂函式支持)取得文章列表,並以 Flexbox 版型顯示。

文章資料與版型處理

  • 透過 get_the_post_thumbnail 取得文章縮圖,若無縮圖則顯示預設佔位元素。
  • 文章分類顯示為角標,若無分類則顯示「文章」。
  • 若文章有特定欄位 ks_post_access 標示有權限,則在角標旁顯示鎖頭圖示。

文章列表輸出範例

foreach ($post_ids as $post_id) {
  $has_access = get_field('ks_post_access', $post_id);
  $thumb_html = get_the_post_thumbnail($post_id, 'medium_large', ['class' => 'fi-img', 'loading'=>'lazy', 'decoding'=>'async']);
  if(empty($thumb_html)) {
    $thumb_html = '<div class="fi-img fi-img--placeholder" aria-hidden="true"></div>';
  }
  $cats = get_the_category($post_id);
  $badge = (!empty($cats) && !is_wp_error($cats)) ? $cats[0]->name : '文章';
  $permalink = get_permalink($post_id);
  $title = get_the_title($post_id);

  echo '<a class="follow-item" href="' . esc_url($permalink) . '">'
       . '<div class="fi-thumb">' . $thumb_html
       . '<div class="fi-badge"><span class="fi-tag">' . esc_html($badge) . '</span>';
  if ($has_access) {
    echo '<span class="fi-access-icon">(鎖頭 SVG)</span>';
  }
  echo '</div></div>'
       . '<div class="fi-content"><h3 class="fi-title">' . esc_html($title) . '</h3></div>'
       . '</a>';
}

實務應用與延伸

此功能適合用於會員制網站,讓使用者能方便管理自己感興趣的文章。可進一步結合 AJAX 技術實現無刷新更新,或加入取消追蹤功能提升互動性。若文章有付費限制,鎖頭圖示能明確提示使用者權限狀態。

常見問題與注意事項

  • get_user_favorites 函式需確認是否有安裝相應收藏功能插件,否則會回傳空陣列。
  • 權限欄位 ks_post_access 為自訂欄位,需確保文章有正確設定。
  • SVG 圖示直接內嵌於 HTML,方便自訂樣式與顯示,避免外部資源依賴。

完整程式碼

// Account > 追蹤清單 tab
add_action('mepr_account_nav', function($action){
  $active = (isset($_GET['action']) && $_GET['action'] === 'user-follow') ? 'mepr-active-nav-tab' : '';
  echo '<span class="mepr-nav-item user-follow ' . esc_attr($active) . '"><a href="/account/?action=user-follow">追蹤文章</a></span>';
});

// Account > 追蹤清單內容:Flex 版型
add_action('mepr_account_nav_content', function($action){
  if($action !== 'user-follow') return;

  // Favorite Posts
  $filters = [
    'post_type' => ['post'],
    'status'    => ['publish'],
  ];
  $post_ids = function_exists('get_user_favorites')
    ? get_user_favorites(get_current_user_id(), null, $filters)
    : [];

  if (empty($post_ids)) {
    echo '
<p>尚無追蹤的文章</p>';
    return;
  }

  echo '<h2 class="follow-title"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 7C8 6.44772 8.44772 6 9 6H23C23.5523 6 24 6.44772 24 7V25.1827C24 25.5564 23.6051 25.798 23.2724 25.6279L17.3659 22.6075C16.5081 22.1689 15.4919 22.1689 14.6341 22.6075L8.72764 25.6279C8.39495 25.798 8 25.5564 8 25.1827V7Z" fill="#131314" stroke="#131314" stroke-width="1.5"/>
<path d="M15.8153 9.44399C15.8837 9.27973 16.1163 9.27973 16.1847 9.44399L17.5401 12.7029C17.5689 12.7721 17.634 12.8194 17.7088 12.8254L21.227 13.1075C21.4044 13.1217 21.4763 13.343 21.3411 13.4587L18.6606 15.7549C18.6037 15.8037 18.5788 15.8802 18.5962 15.9532L19.4151 19.3864C19.4564 19.5594 19.2682 19.6962 19.1163 19.6035L16.1043 17.7637C16.0402 17.7246 15.9598 17.7246 15.8957 17.7637L12.8837 19.6035C12.7318 19.6962 12.5436 19.5594 12.5849 19.3864L13.4038 15.9532C13.4212 15.8802 13.3963 15.8037 13.3394 15.7549L10.6589 13.4587C10.5237 13.343 10.5957 13.1217 10.773 13.1075L14.2912 12.8254C14.366 12.8194 14.4311 12.7721 14.4599 12.7029L15.8153 9.44399Z" fill="white"/>
</svg>我的追蹤文章</h2>';

  echo '<div class="follow-list">';
  foreach ($post_ids as $post_id) {
    // 檢查是否有 access 權限
    $has_access = get_field('ks_post_access', $post_id);

    // 縮圖
    $thumb_html = get_the_post_thumbnail(
      $post_id,
      'medium_large',
      ['class' => 'fi-img', 'loading'=>'lazy', 'decoding'=>'async']
    );
    if(empty($thumb_html)){
      $thumb_html = '<div class="fi-img fi-img--placeholder" aria-hidden="true"></div>';
    }

    // 角標:第一個分類,無則顯示「文章」
    $cats  = get_the_category($post_id);
    $badge = (!empty($cats) && !is_wp_error($cats)) ? $cats[0]->name : '文章';
    $permalink = get_permalink($post_id);
    $title     = get_the_title($post_id);

    echo '<a class="follow-item" href="' . esc_url($permalink) . '">'
         . '<div class="fi-thumb">'
         .   $thumb_html .   
        '<div class="fi-badge">
        <span class="fi-tag">' . esc_html($badge) . '</span>';
        // 如果有 access 權限,顯示鎖頭圖標 
        if ($has_access) { 
            echo '<span class="fi-access-icon"><svg width="10" height="14" viewBox="0 0 10 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 0C6.65685 0 8 1.34315 8 3V5H9C9.55228 5 10 5.44772 10 6V13C10 13.5523 9.55229 14 9 14H1C0.447715 14 4.02663e-09 13.5523 0 13V6C0 5.44772 0.447715 5 1 5H2V3C2 1.34315 3.34315 0 5 0ZM5.17969 6.86328C5.10631 6.71493 4.8948 6.71508 4.82129 6.86328L4.16504 8.19238C4.13591 8.25128 4.07967 8.29225 4.01465 8.30176L2.54785 8.51465C2.38396 8.53863 2.31885 8.74079 2.4375 8.85645L3.49805 9.89062C3.54512 9.93657 3.56678 10.0025 3.55566 10.0674L3.30566 11.5273C3.27776 11.6906 3.44903 11.8153 3.5957 11.7383L4.90723 11.0488C4.96546 11.0183 5.03554 11.0182 5.09375 11.0488L6.40527 11.7383C6.55191 11.8152 6.72321 11.6906 6.69531 11.5273L6.44434 10.0674C6.43323 10.0026 6.45503 9.93656 6.50195 9.89062L7.56348 8.85645C7.68218 8.74074 7.61618 8.5385 7.45215 8.51465L5.98633 8.30176C5.92122 8.2923 5.86411 8.25136 5.83496 8.19238L5.17969 6.86328ZM5 1C3.89543 1 3 1.89543 3 3V5H7V3C7 1.89543 6.10457 1 5 1Z" fill="white"/></svg></span>'; 
    }

            echo '</div>';

    echo   '</div>'
         . '<div class="fi-content">'
         .   '<h3 class="fi-title">' . esc_html($title) . '</h3>'
         . '</div>'
         . '</a>';
  }
  echo '</div>';
});