前言
本篇文章介紹如何在 WordPress 中取得特定用戶曾留言過的文章列表,並且顯示該用戶在每篇文章的留言數量。這段程式碼適合用於會員系統或社群互動功能,方便開發者在會員後台展示用戶的互動紀錄。目標讀者為具備 PHP 和 WordPress 基礎,想了解如何結合資料庫查詢與文章篩選的工程師或自學者。
取得用戶留言過的文章 ID
透過 WordPress 的全域變數 $wpdb,使用 SQL 查詢 comments 資料表,取得指定用戶的留言所對應的文章 ID,並去除重複。此函式回傳整數陣列。
function ks_get_user_commented_post_ids( $user_id ) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT DISTINCT comment_post_ID FROM {$wpdb->comments} WHERE user_id = %d",
$user_id
);
$results = $wpdb->get_col( $query );
return array_map( 'intval', $results );
}
為什麼使用 SQL 查詢?
直接查詢資料庫效率較高,避免使用 WordPress 內建函式多次查詢留言,尤其用戶留言量大時更具效能優勢。
取得用戶留言過且仍有效的文章物件
此函式會先取得用戶留言過的文章 ID,再逐一檢查文章狀態是否為「已發佈」且文章類型為「post」,最後使用 get_posts 取得完整文章物件陣列,並以發佈日期排序。
function ks_get_user_commented_posts( $user_id = 0 ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! $user_id ) {
return array();
}
$commented_post_ids = ks_get_user_commented_post_ids( $user_id );
if ( empty( $commented_post_ids ) ) {
return array();
}
$valid_post_ids = array();
foreach ($commented_post_ids as $post_id) {
$post = get_post($post_id);
if ($post && $post->post_status === 'publish' && $post->post_type === 'post') {
$valid_post_ids[] = $post_id;
}
}
if (empty($valid_post_ids)) {
return array();
}
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => -1,
'post__in' => $valid_post_ids,
'orderby' => 'date',
'order' => 'DESC'
) );
return $posts;
}
為什麼要篩選文章狀態?
確保只顯示目前可見的文章,避免用戶看到草稿或已刪除的文章,提升使用體驗與資料正確性。
取得用戶在指定文章的留言數量
同樣利用 $wpdb 執行 SQL 查詢,計算該用戶在特定文章的留言數,回傳整數。
function ks_get_user_comment_count_for_post( $post_id, $user_id = 0 ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! $user_id ) {
return 0;
}
global $wpdb;
$count = $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_post_ID = %d AND user_id = %d",
$post_id,
$user_id
) );
return intval($count);
}
在 MemberPress 會員後台新增「我的留言」分頁
透過 mepr_account_nav 與 mepr_account_nav_content 兩個鉤子,分別新增導覽選單與內容區塊,顯示用戶留言過的文章列表。
導覽選單新增項目
add_action('mepr_account_nav', function($action) {
$active = (isset($_GET['action']) && $_GET['action'] === 'user-commented') ? 'mepr-active-nav-tab' : '';
echo '<span class="mepr-nav-item user-commented ' . esc_attr($active) . '">';
echo '<a href="/account/?action=user-commented">我的留言</a>';
echo '</span>';
});
顯示留言文章列表內容
- 先取得當前用戶留言過的文章
- 無留言時顯示提示文字
- 有留言時列出文章縮圖、分類、留言數與標題連結
- 若文章有額外權限設定,顯示鎖頭圖示
add_action('mepr_account_nav_content', function($action) {
if ($action !== 'user-commented') {
return;
}
$current_user_id = get_current_user_id();
$commented_posts = ks_get_user_commented_posts($current_user_id);
if (empty($commented_posts)) {
echo '<div class="no-comments-message">';
echo '
<p>尚無留言過的文章</p>';
echo '</div>';
return;
}
echo <<<HTML
<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>
HTML;
echo '<div class="my-comments-list">';
foreach ($commented_posts as $post) {
$post_id = $post->ID;
$thumbnail = has_post_thumbnail($post_id) ? get_the_post_thumbnail($post_id, 'medium', array('class' => 'post-thumb', 'loading' => 'lazy')) : '<div class="post-thumb post-thumb-default"></div>';
$categories = get_the_category($post_id);
$category_name = (!empty($categories)) ? $categories[0]->name : '文章';
$comment_count = ks_get_user_comment_count_for_post($post_id, $current_user_id);
$post_title = get_the_title($post_id);
$post_link = get_permalink($post_id);
$has_access = get_field('ks_post_access', $post_id);
echo '<div class="comment-post-item">';
echo '<div class="post-thumb-container">';
echo $thumbnail;
echo '<div class="fi-badge"><span class="fi-tag">' . esc_html($category_name) . '</span>';
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>';
echo '<div class="post-content-area">';
echo '<h3 class="post-title-link">';
echo '<a href="' . esc_url($post_link) . '">' . esc_html($post_title) . '</a>';
echo '</h3>';
echo '<div class="post-meta-info">';
echo '<span class="comment-count-info">' . $comment_count . ' 則留言</span>';
echo '</div>';
echo '</div>';
echo '</div>';
}
echo '</div>';
});
實務應用與優化方向
- 可結合 AJAX 實現分頁或動態載入,避免一次載入大量文章造成頁面卡頓
- 留言數量可用快取機制減少資料庫查詢次數
- 依需求擴充篩選條件,如文章分類、日期區間等
- 針對會員權限做更細緻的存取控制,提升安全性
完整程式碼
<?php
/**
* 獲取用戶留言過的文章ID陣列
*/
function ks_get_user_commented_post_ids( $user_id ) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT DISTINCT comment_post_ID FROM {$wpdb->comments} WHERE user_id = %d",
$user_id
);
$results = $wpdb->get_col( $query );
return array_map( 'intval', $results );
}
/**
* 獲取用戶留言過的文章
*/
function ks_get_user_commented_posts( $user_id = 0 ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! $user_id ) {
return array();
}
$commented_post_ids = ks_get_user_commented_post_ids( $user_id );
if ( empty( $commented_post_ids ) ) {
return array();
}
$valid_post_ids = array();
foreach ($commented_post_ids as $post_id) {
$post = get_post($post_id);
if ($post && $post->post_status === 'publish' && $post->post_type === 'post') {
$valid_post_ids[] = $post_id;
}
}
if (empty($valid_post_ids)) {
return array();
}
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => -1,
'post__in' => $valid_post_ids,
'orderby' => 'date',
'order' => 'DESC'
) );
return $posts;
}
/**
* 獲取用戶在特定文章的留言數量
*/
function ks_get_user_comment_count_for_post( $post_id, $user_id = 0 ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! $user_id ) {
return 0;
}
global $wpdb;
$count = $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_post_ID = %d AND user_id = %d",
$post_id,
$user_id
) );
return intval($count);
}
// MemberPress 會員後台整合
add_action('mepr_account_nav', function($action) {
$active = (isset($_GET['action']) && $_GET['action'] === 'user-commented') ? 'mepr-active-nav-tab' : '';
echo '<span class="mepr-nav-item user-commented ' . esc_attr($active) . '">';
echo '<a href="/account/?action=user-commented">我的留言</a>';
echo '</span>';
});
add_action('mepr_account_nav_content', function($action) {
if ($action !== 'user-commented') {
return;
}
$current_user_id = get_current_user_id();
$commented_posts = ks_get_user_commented_posts($current_user_id);
if (empty($commented_posts)) {
echo '<div class="no-comments-message">';
echo '
<p>尚無留言過的文章</p>';
echo '</div>';
return;
}
echo <<<HTML
<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>
HTML;
echo '<div class="my-comments-list">';
foreach ($commented_posts as $post) {
$post_id = $post->ID;
$thumbnail = '';
if (has_post_thumbnail($post_id)) {
$thumbnail = get_the_post_thumbnail($post_id, 'medium', array(
'class' => 'post-thumb',
'loading' => 'lazy'
));
} else {
$thumbnail = '<div class="post-thumb post-thumb-default"></div>';
}
$categories = get_the_category($post_id);
$category_name = (!empty($categories)) ? $categories[0]->name : '文章';
$comment_count = ks_get_user_comment_count_for_post($post_id, $current_user_id);
$post_title = get_the_title($post_id);
$post_link = get_permalink($post_id);
$has_access = get_field('ks_post_access', $post_id);
echo '<div class="comment-post-item">';
echo '<div class="post-thumb-container">';
echo $thumbnail;
echo '<div class="fi-badge">
<span class="fi-tag">' . esc_html($category_name) . '</span>';
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>';
echo '<div class="post-content-area">';
echo '<h3 class="post-title-link">';
echo '<a href="' . esc_url($post_link) . '">' . esc_html($post_title) . '</a>';
echo '</h3>';
echo '<div class="post-meta-info">';
echo '<span class="comment-count-info">' . $comment_count . ' 則留言</span>';
echo '</div>';
echo '</div>';
echo '</div>';
}
echo '</div>';
});