<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>後台功能 &#8211; 小豬日常</title>
	<atom:link href="https://piglife.tw/tag/%e5%be%8c%e5%8f%b0%e5%8a%9f%e8%83%bd/feed/" rel="self" type="application/rss+xml" />
	<link>https://piglife.tw</link>
	<description>Hello World，一個紀錄生活與學習的地方</description>
	<lastBuildDate>Mon, 22 Dec 2025 06:10:38 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://piglife.tw/wp-content/uploads/2017/10/cropped-logo-1-32x32.png</url>
	<title>後台功能 &#8211; 小豬日常</title>
	<link>https://piglife.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WordPress 自訂文章類型批次內容關鍵字取代工具實作</title>
		<link>https://piglife.tw/technical-notes/wordpress-solution-content-replace/</link>
					<comments>https://piglife.tw/technical-notes/wordpress-solution-content-replace/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Sun, 21 Dec 2025 22:20:56 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[後台功能]]></category>
		<category><![CDATA[批次替換]]></category>
		<category><![CDATA[自訂文章類型]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/wordpress-solution-content-replace/</guid>

					<description><![CDATA[介紹如何在 WordPress 自訂文章類型 solution 中新增後台子選單，實作批次搜尋並替換...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WordPress 後台管理自訂文章類型（Custom Post Type）時，有時需要快速批次更新文章內容中的特定字串。這段程式碼示範如何在名為 <code>solution</code> 的自訂文章類型新增一個子選單，提供後台管理者輸入舊字串與新字串，並批次替換所有相關文章內容中的文字。適合有基本 WordPress 開發經驗，想要自訂後台功能以提升內容維護效率的工程師或自學者。</p>
<h2 class="wp-block-heading">新增子選單介面</h2>
<p>利用 <code>admin_menu</code> action 新增子選單，並限定該選單只在 <code>solution</code> 文章類型下顯示。權限設定為 <code>edit_posts</code>，確保只有有編輯文章權限的使用者能操作。</p>
<pre><code class="lang-php language-php php">add_action(&#039;admin_menu&#039;, function () {
    if (!post_type_exists(&#039;solution&#039;)) {
        return;
    }

    add_submenu_page(
        &#039;edit.php?post_type=solution&#039;,
        &#039;內容關鍵字取代&#039;,
        &#039;內容關鍵字取代&#039;,
        &#039;edit_posts&#039;,
        &#039;solution-content-replace&#039;,
        &#039;solution_content_replace_page&#039;
    );
});</code></pre>
<p>這段程式碼確保子選單只會在 <code>solution</code> 文章類型的管理頁面出現，並且點擊後會呼叫 <code>solution_content_replace_page</code> 函式來渲染頁面。</p>
<h2 class="wp-block-heading">後台頁面與表單處理</h2>
<p><code>solution_content_replace_page</code> 函式負責顯示輸入表單與處理替換邏輯。首先檢查使用者權限，避免未授權存取。接著使用 WordPress 的 Nonce 機制保障表單安全。</p>
<pre><code class="lang-php language-php php">if (!current_user_can(&#039;edit_posts&#039;)) {
    wp_die(&#039;沒有權限。&#039;);
}

if (isset($_POST[&#039;solution_replace_submit&#039;])) {
    check_admin_referer(&#039;solution_replace_action&#039;, &#039;solution_replace_nonce&#039;);

    $old = isset($_POST[&#039;old_keyword&#039;]) ? sanitize_text_field($_POST[&#039;old_keyword&#039;]) : &#039;&#039;;
    $new = isset($_POST[&#039;new_keyword&#039;]) ? sanitize_text_field($_POST[&#039;new_keyword&#039;]) : &#039;&#039;;

    // 進行批次替換
}</code></pre>
<p>使用者輸入的舊字串與新字串會經過 <code>sanitize_text_field</code> 清理，避免 XSS 或其他安全問題。</p>
<h2 class="wp-block-heading">批次搜尋與替換邏輯</h2>
<p>當舊字串不為空時，使用 <code>WP_Query</code> 撈取所有 <code>solution</code> 文章的 ID，並逐篇讀取內容。若文章內容包含舊字串，則使用 <code>str_replace</code> 替換後，呼叫 <code>wp_update_post</code> 更新文章。</p>
<pre><code class="lang-php language-php php">$query = new WP_Query([
    &#039;post_type&#039;      =&gt; &#039;solution&#039;,
    &#039;post_status&#039;    =&gt; &#039;any&#039;,
    &#039;posts_per_page&#039; =&gt; -1,
    &#039;fields&#039;         =&gt; &#039;ids&#039;,
]);

foreach ($query-&gt;posts as $post_id) {
    $post = get_post($post_id);
    if (!$post) {
        continue;
    }

    $content = $post-&gt;post_content;

    if (mb_strpos($content, $old) !== false) {
        $updated_content = str_replace($old, $new, $content);

        wp_update_post([
            &#039;ID&#039;           =&gt; $post_id,
            &#039;post_content&#039; =&gt; $updated_content
        ]);

        $results[] = [
            &#039;id&#039;     =&gt; $post_id,
            &#039;title&#039;  =&gt; $post-&gt;post_title,
            &#039;status&#039; =&gt; &#039;已替換&#039;
        ];
    }
}
wp_reset_postdata();</code></pre>
<h2 class="wp-block-heading">結果呈現與使用者體驗</h2>
<p>頁面會根據執行狀態顯示不同訊息：</p>
<ul>
<li>若未輸入舊字串，提醒使用者必須填寫。</li>
<li>若找不到包含舊字串的文章，顯示資訊提示。</li>
<li>若有替換成功的文章，列出文章 ID、標題、狀態與編輯連結，方便管理者後續檢視。</li>
</ul>
<p>這樣的設計讓使用者能清楚知道批次操作的結果，並快速跳轉編輯。</p>
<h2 class="wp-block-heading">實務應用與優化建議</h2>
<p>此功能適合內容量大且需定期文字修正的網站，如產品說明、技術文件等。未來可擴充：</p>
<ul>
<li>支援正則表達式替換，提高靈活度。</li>
<li>加入替換前後內容差異預覽，降低誤替換風險。</li>
<li>加入分頁或批次處理，避免大量文章一次更新造成伺服器負擔。</li>
</ul>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>請確認使用者權限設定正確，避免誤用。</li>
<li>替換操作無法還原，建議先備份資料庫。</li>
<li>文字替換為純字串，不支援 HTML 或多語系複雜處理。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-php language-php php">&lt;?php
// 在 solution post type 底下新增子選單：內容關鍵字取代
add_action(&#039;admin_menu&#039;, function () {
    if (!post_type_exists(&#039;solution&#039;)) {
        return;
    }

    add_submenu_page(
        &#039;edit.php?post_type=solution&#039;,
        &#039;內容關鍵字取代&#039;,
        &#039;內容關鍵字取代&#039;,
        &#039;edit_posts&#039;,
        &#039;solution-content-replace&#039;,
        &#039;solution_content_replace_page&#039;
    );
});

// 後台頁面
function solution_content_replace_page() {
    if (!current_user_can(&#039;edit_posts&#039;)) {
        wp_die(&#039;沒有權限。&#039;);
    }

    $old = &#039;&#039;;
    $new = &#039;&#039;;
    $results = [];
    $executed = false;

    if (isset($_POST[&#039;solution_replace_submit&#039;])) {
        check_admin_referer(&#039;solution_replace_action&#039;, &#039;solution_replace_nonce&#039;);

        $old = isset($_POST[&#039;old_keyword&#039;]) ? sanitize_text_field($_POST[&#039;old_keyword&#039;]) : &#039;&#039;;
        $new = isset($_POST[&#039;new_keyword&#039;]) ? sanitize_text_field($_POST[&#039;new_keyword&#039;]) : &#039;&#039;;

        $executed = true;

        if ($old !== &#039;&#039;) {
            $query = new WP_Query([
                &#039;post_type&#039;      =&gt; &#039;solution&#039;,
                &#039;post_status&#039;    =&gt; &#039;any&#039;,
                &#039;posts_per_page&#039; =&gt; -1,
                &#039;fields&#039;         =&gt; &#039;ids&#039;,
            ]);

            foreach ($query-&gt;posts as $post_id) {
                $post = get_post($post_id);
                if (!$post) {
                    continue;
                }

                $content = $post-&gt;post_content;

                // 檢查舊字串是否存在
                if (mb_strpos($content, $old) !== false) {
                    // 替換
                    $updated_content = str_replace($old, $new, $content);

                    // 更新文章
                    wp_update_post([
                        &#039;ID&#039;           =&gt; $post_id,
                        &#039;post_content&#039; =&gt; $updated_content
                    ]);

                    $results[] = [
                        &#039;id&#039;     =&gt; $post_id,
                        &#039;title&#039;  =&gt; $post-&gt;post_title,
                        &#039;status&#039; =&gt; &#039;已替換&#039;
                    ];
                }
            }
            wp_reset_postdata();
        }
    }
    ?&gt;

&lt;div class=&quot;wrap&quot;&gt;

&lt;h2&gt;Solution &ndash; 內容關鍵字取代&lt;/h2&gt;

        &lt;form method=&quot;post&quot;&gt;
            &lt;?php wp_nonce_field(&#039;solution_replace_action&#039;, &#039;solution_replace_nonce&#039;); ?&gt;

            &lt;table class=&quot;form-table&quot;&gt;

&lt;tr&gt;

&lt;th&gt;&lt;label for=&quot;old_keyword&quot;&gt;要搜尋的字（舊字串）&lt;/label&gt;&lt;/th&gt;

&lt;td&gt;
                        &lt;input type=&quot;text&quot; id=&quot;old_keyword&quot; name=&quot;old_keyword&quot; class=&quot;regular-text&quot;
                               value=&quot;&lt;?php echo esc_attr($old); ?&gt;&quot;
                               placeholder=&quot;例如：舊字串&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;

&lt;tr&gt;

&lt;th&gt;&lt;label for=&quot;new_keyword&quot;&gt;替換成（新字串）&lt;/label&gt;&lt;/th&gt;

&lt;td&gt;
                        &lt;input type=&quot;text&quot; id=&quot;new_keyword&quot; name=&quot;new_keyword&quot; class=&quot;regular-text&quot;
                               value=&quot;&lt;?php echo esc_attr($new); ?&gt;&quot;
                               placeholder=&quot;例如：新字串（可空白）&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;

            &lt;?php submit_button(&#039;開始批次取代&#039;, &#039;primary&#039;, &#039;solution_replace_submit&#039;); ?&gt;
        &lt;/form&gt;

        &lt;?php if ($executed): ?&gt;

&lt;h2&gt;取代結果&lt;/h2&gt;

            &lt;?php if ($old === &#039;&#039;): ?&gt;
                &lt;div class=&quot;notice notice-warning&quot;&gt;&lt;p&gt;請輸入要搜尋的舊字串。&lt;/p&gt;&lt;/div&gt;

            &lt;?php elseif (empty($results)): ?&gt;
                &lt;div class=&quot;notice notice-info&quot;&gt;&lt;p&gt;沒有任何文章包含「&lt;?php echo esc_html($old); ?&gt;」。&lt;/p&gt;&lt;/div&gt;

            &lt;?php else: ?&gt;
                &lt;table class=&quot;widefat striped&quot;&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;ID&lt;/th&gt;

&lt;th&gt;標題&lt;/th&gt;

&lt;th&gt;狀態&lt;/th&gt;

&lt;th&gt;編輯連結&lt;/th&gt;
                    &lt;/tr&gt;
                    &lt;/thead&gt;

&lt;tbody&gt;
                    &lt;?php foreach ($results as $r): ?&gt;

&lt;tr&gt;

&lt;td&gt;&lt;?php echo esc_html($r[&#039;id&#039;]); ?&gt;&lt;/td&gt;

&lt;td&gt;&lt;?php echo esc_html($r[&#039;title&#039;]); ?&gt;&lt;/td&gt;

&lt;td&gt;&lt;?php echo esc_html($r[&#039;status&#039;]); ?&gt;&lt;/td&gt;

&lt;td&gt;&lt;a href=&quot;&lt;?php echo get_edit_post_link($r[&#039;id&#039;]); ?&gt;&quot; class=&quot;button button-small&quot;&gt;編輯&lt;/a&gt;&lt;/td&gt;
                        &lt;/tr&gt;
                    &lt;?php endforeach; ?&gt;
                    &lt;/tbody&gt;
                &lt;/table&gt;
            &lt;?php endif; ?&gt;

        &lt;?php endif; ?&gt;
    &lt;/div&gt;

    &lt;?php
}</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/wordpress-solution-content-replace/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
