<?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>woocommerce &#8211; 小豬日常</title>
	<atom:link href="https://piglife.tw/tag/woocommerce/feed/" rel="self" type="application/rss+xml" />
	<link>https://piglife.tw</link>
	<description>Hello World，一個紀錄生活與學習的地方</description>
	<lastBuildDate>Sun, 28 Dec 2025 22:20:33 +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>woocommerce &#8211; 小豬日常</title>
	<link>https://piglife.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WooCommerce 後台訂單自訂欄位資料儲存實作說明</title>
		<link>https://piglife.tw/technical-notes/woocommerce-order-meta-save/</link>
					<comments>https://piglife.tw/technical-notes/woocommerce-order-meta-save/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Sun, 28 Dec 2025 22:20:33 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[後台欄位儲存]]></category>
		<category><![CDATA[訂單自訂欄位]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/woocommerce-order-meta-save/</guid>

					<description><![CDATA[說明如何在 WooCommerce 後台訂單編輯頁面保存自訂欄位資料，透過安全且兼容新版訂單存儲的方...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WooCommerce 訂單後台管理時，常會需要額外新增自訂欄位以收集特定資訊，例如客戶的聯絡電話。這段程式碼示範如何在訂單編輯頁面保存自訂欄位資料，確保資料能正確存入訂單元資料中。適合已熟悉 WooCommerce 且想擴充後台訂單欄位功能的工程師或自學者。</p>
<h2 class="wp-block-heading">為什麼需要自訂欄位儲存機制</h2>
<p>WooCommerce 預設訂單資料結構無法涵蓋所有業務需求，因此經常會透過自訂欄位來擴充。這些欄位必須在後台訂單編輯時能夠被正確讀取與保存，否則資料會遺失。</p>
<h2 class="wp-block-heading">使用 woocommerce_process_shop_order_meta 鉤子</h2>
<p>這個 action 鉤子會在 WooCommerce 處理訂單後台編輯表單時觸發，適合用來攔截並保存自訂欄位資料。</p>
<pre><code class="lang-php language-php php">add_action(&#039;woocommerce_process_shop_order_meta&#039;, function ($order_id) {
    // 權限檢查，避免非授權使用者修改訂單
    if (!current_user_can(&#039;edit_shop_order&#039;, $order_id))
        return;

    $key = &#039;_shipping_phone&#039;; // 自訂欄位名稱

    // 確認表單有送出該欄位
    if (!isset($_POST[$key]))
        return;

    // 清理輸入資料，避免 XSS 或其他注入風險
    $value = wc_clean(wp_unslash($_POST[$key]));

    // 取得訂單物件
    $order = wc_get_order($order_id);
    if (!$order)
        return;

    // 使用 WC_Order API 更新訂單元資料
    $order-&gt;update_meta_data($key, $value);
    $order-&gt;save(); // 必須呼叫 save() 才會寫入資料庫
}, 50);</code></pre>
<h3 class="wp-block-heading">關鍵說明</h3>
<ul>
<li><code>current_user_can</code> 用來確保只有有編輯訂單權限的使用者能執行更新。</li>
<li><code>wc_clean</code> 和 <code>wp_unslash</code> 是 WordPress 與 WooCommerce 提供的安全函式，確保輸入資料安全。</li>
<li>使用 <code>update_meta_data</code> 與 <code>save</code> 是 WooCommerce 推薦的寫入方式，兼容新版 HPOS（高效訂單存儲系統）與舊版 postmeta。</li>
</ul>
<h2 class="wp-block-heading">實務應用與延伸</h2>
<p>此方法可擴充至任何自訂欄位，只要對應修改 <code>$key</code> 與表單名稱即可。實務中，還可搭配後台欄位輸入介面（如使用 <code>woocommerce_admin_order_data_after_billing_address</code> 鉤子）來完整實作自訂欄位的讀寫。</p>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>忘記呼叫 <code>$order-&gt;save()</code> 將導致資料無法寫入。</li>
<li>欄位名稱建議加底線開頭避免與 WooCommerce 原生欄位衝突。</li>
<li>權限檢查不可省略，避免安全問題。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-php language-php php">&lt;?php
add_action(&#039;woocommerce_process_shop_order_meta&#039;, function ($order_id) {
    if (!current_user_can(&#039;edit_shop_order&#039;, $order_id))
        return;

    $key = &#039;_shipping_phone&#039;;

    if (!isset($_POST[$key]))
        return;

    $value = wc_clean(wp_unslash($_POST[$key]));

    $order = wc_get_order($order_id);
    if (!$order)
        return;

    $order-&gt;update_meta_data($key, $value);
    $order-&gt;save();
}, 50);</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/woocommerce-order-meta-save/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WooCommerce 後台商品列表新增複製購物車連結並支援優惠券輸入功能</title>
		<link>https://piglife.tw/technical-notes/woocommerce-copy-cart-link-coupon/</link>
					<comments>https://piglife.tw/technical-notes/woocommerce-copy-cart-link-coupon/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Tue, 23 Dec 2025 22:21:03 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[優惠券]]></category>
		<category><![CDATA[剪貼簿操作]]></category>
		<category><![CDATA[後台自訂欄位]]></category>
		<category><![CDATA[複製連結]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/woocommerce-copy-cart-link-coupon/</guid>

					<description><![CDATA[介紹如何在 WooCommerce 後台商品列表新增一欄複製加入購物車連結的按鈕，並支援彈窗輸入優惠...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WooCommerce 後台商品列表中，管理者常需要快速取得商品的「加入購物車」連結以便分享或推廣。這段程式碼實作了在商品列表中新增一欄「複製課程連結」按鈕，點擊後可彈出視窗讓使用者輸入優惠券代碼，並將優惠券參數附加到連結中，最後將完整連結複製到剪貼簿。這對於需要快速產生帶優惠券的購物車連結的電商管理者非常實用。</p>
<h2 class="wp-block-heading">新增自訂欄位顯示複製按鈕</h2>
<p>利用 <code>manage_edit-product_columns</code> 過濾器新增一欄「複製課程連結」，並透過 <code>manage_product_posts_custom_column</code> 動作在該欄位輸出一個帶有商品加入購物車 URL 的按鈕。URL 是以網站首頁 URL 加上 <code>/cart/?add-to-cart=商品ID</code> 組成。</p>
<pre><code class="lang-php language-php php">add_filter(&#039;manage_edit-product_columns&#039;, &#039;add_copy_link_column&#039;);
function add_copy_link_column($columns) {
    $columns[&#039;copy_add_to_cart&#039;] = __(&#039;複製課程連結&#039;, &#039;woocommerce&#039;);
    return $columns;
}

add_action(&#039;manage_product_posts_custom_column&#039;, &#039;show_copy_link_column_content&#039;, 10, 2);
function show_copy_link_column_content($column, $post_id) {
    if ($column === &#039;copy_add_to_cart&#039;) {
        $product_id = (int) $post_id;
        $site_url = my_copy_cart_site_url();
        $add_to_cart_url = $site_url . &#039;/cart/?add-to-cart=&#039; . $product_id;
        echo &#039;&lt;button type=&quot;button&quot; class=&quot;button copy-cart-link&quot; data-url=&quot;&#039; . esc_attr($add_to_cart_url) . &#039;&quot; title=&quot;點擊複製連結&quot;&gt;複製&lt;/button&gt;&#039;;
    }
}</code></pre>
<h2 class="wp-block-heading">客製化 JavaScript 處理複製邏輯與優惠券輸入</h2>
<p>在後台商品列表頁尾插入 JavaScript 和 CSS，當使用者點擊「複製」按鈕時：</p>
<ol>
<li>透過 <code>window.prompt</code> 詢問是否輸入優惠券代碼，可留空。</li>
<li>根據輸入結果組合最終 URL，若有優惠券則加上 <code>&amp;wt_coupon=優惠券代碼</code>。</li>
<li>使用 Clipboard API 複製連結，若不支援則使用傳統 <code>execCommand</code> 備援。</li>
<li>複製成功會顯示成功提示，失敗則顯示錯誤提示並提供手動複製。</li>
</ol>
<pre><code class="lang-js language-js js">function buildFinalUrl(baseUrl) {
    var coupon = window.prompt(&#039;是否要加入優惠券？\n(可留空，直接按確定即可)&#039;, &#039;&#039;);
    if (coupon === null) return baseUrl;
    coupon = String(coupon || &#039;&#039;).trim();
    if (!coupon) return baseUrl;
    var joiner = (baseUrl.indexOf(&#039;?&#039;) === -1) ? &#039;?&#039; : &#039;&amp;&#039;;
    return baseUrl + joiner + &#039;wt_coupon=&#039; + encodeURIComponent(coupon);
}

$(document).on(&#039;click&#039;, &#039;.copy-cart-link&#039;, function(e) {
    e.preventDefault();
    var button = $(this);
    var baseUrl = button.data(&#039;url&#039;);
    var originalHtml = button.html();
    if (button.hasClass(&#039;copying&#039;)) return;
    button.addClass(&#039;copying&#039;);
    var finalUrl = buildFinalUrl(baseUrl);
    if (navigator.clipboard &amp;&amp; window.isSecureContext) {
        navigator.clipboard.writeText(finalUrl).then(function() {
            showCopySuccess(button, originalHtml);
        }).catch(function() {
            fallbackCopyTextToClipboard(finalUrl, button, originalHtml);
        });
    } else {
        fallbackCopyTextToClipboard(finalUrl, button, originalHtml);
    }
});</code></pre>
<h2 class="wp-block-heading">快速動作連結整合</h2>
<p>除了欄位按鈕外，也在商品標題下方的快速動作連結加入「複製購物車連結」，提升操作便利性。此連結同樣帶有商品加入購物車的 URL，並套用相同的 JavaScript 行為。</p>
<pre><code class="lang-php language-php php">add_filter(&#039;post_row_actions&#039;, &#039;add_copy_link_quick_action&#039;, 10, 2);
function add_copy_link_quick_action($actions, $post) {
    if ($post-&gt;post_type === &#039;product&#039;) {
        $product_id = (int) $post-&gt;ID;
        $site_url = my_copy_cart_site_url();
        $add_to_cart_url = $site_url . &#039;/cart/?add-to-cart=&#039; . $product_id;
        $actions[&#039;copy_cart_link&#039;] = &#039;&lt;a href=&quot;#&quot; class=&quot;copy-cart-link&quot; data-url=&quot;&#039; . esc_attr($add_to_cart_url) . &#039;&quot; style=&quot;color:#0073aa;&quot;&gt;📋 複製購物車連結&lt;/a&gt;&#039;;
    }
    return $actions;
}</code></pre>
<h2 class="wp-block-heading">實務應用與優化建議</h2>
<p>此功能適合需要快速產生帶優惠券的購物車連結的 WooCommerce 商店管理員，方便推廣或客服回覆。未來可優化：</p>
<ul>
<li>將網站 URL 改為動態取得，避免硬編碼。</li>
<li>增加複製成功的視覺動畫提升 UX。</li>
<li>支援多種優惠券參數或其他自訂參數。</li>
<li>對於大量商品列表，可考慮優化前端效能。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-php language-php php">&lt;?php
/**
 * WooCommerce 後台商品列表添加複製 add-to-cart 連結的功能 + 可選優惠券
 * 適用於 WPCode（PHP Snippet / Run Everywhere 或 Admin Only）
 *
 * 功能：
 * - 商品列表新增一欄「複製課程連結」
 * - 點按後彈出視窗詢問優惠券（可留空）
 * - 若有填，複製的 URL 會加上：&amp;wt_coupon=COUPON
 * - 同時支援「快速動作」複製
 */

/** 你網站的網域（建議改成動態，不用寫死） */
function my_copy_cart_site_url() {
    return home_url();
}

// 在商品列表中添加自定義列
add_filter(&#039;manage_edit-product_columns&#039;, &#039;add_copy_link_column&#039;);
function add_copy_link_column($columns) {
    $columns[&#039;copy_add_to_cart&#039;] = __(&#039;複製課程連結&#039;, &#039;woocommerce&#039;);
    return $columns;
}

// 顯示列內容
add_action(&#039;manage_product_posts_custom_column&#039;, &#039;show_copy_link_column_content&#039;, 10, 2);
function show_copy_link_column_content($column, $post_id) {
    if ($column === &#039;copy_add_to_cart&#039;) {
        $product_id = (int) $post_id;

        $site_url = my_copy_cart_site_url();
        $add_to_cart_url = $site_url . &#039;/cart/?add-to-cart=&#039; . $product_id;

        echo &#039;&lt;button type=&quot;button&quot; class=&quot;button copy-cart-link&quot; data-url=&quot;&#039; . esc_attr($add_to_cart_url) . &#039;&quot; title=&quot;點擊複製連結&quot;&gt;
                 複製
              &lt;/button&gt;&#039;;
    }
}

// 添加 JavaScript 和 CSS
add_action(&#039;admin_footer&#039;, &#039;copy_link_admin_script&#039;);
function copy_link_admin_script() {
    $screen = get_current_screen();
    if (!$screen || $screen-&gt;id !== &#039;edit-product&#039;) {
        return;
    }
    ?&gt;

&lt;style&gt;
        .copy-cart-link {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 4px 8px;
            font-size: 12px;
            line-height: 1.4;
            border-radius: 3px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .copy-cart-link:hover {
            background-color: #000000;
            color: white;
            transform: translateY(-1px);
        }
        .copy-cart-link .dashicons {
            font-size: 14px;
            width: 14px;
            height: 14px;
        }
        .column-copy_add_to_cart {
            width: 110px;
            text-align: center;
        }
        @media screen and (max-width: 782px) {
            .copy-cart-link {
                padding: 6px 10px;
                font-size: 13px;
            }
        }
    &lt;/style&gt;

    &lt;script type=&quot;text/javascript&quot;&gt;
        jQuery(document).ready(function($) {

            // 組裝最終要複製的 URL（可選 wt_coupon）
            function buildFinalUrl(baseUrl) {
                // 1) 詢問優惠券（可留空）
                var coupon = window.prompt(&#039;是否要加入優惠券？\n(可留空，直接按確定即可)&#039;, &#039;&#039;);

                // 使用者按取消 -&gt; 仍照原本複製（你也可以改成 return null 代表不複製）
                if (coupon === null) {
                    return baseUrl;
                }

                coupon = String(coupon || &#039;&#039;).trim();
                if (!coupon) {
                    return baseUrl;
                }

                // 2) 已有 ?add-to-cart=...，直接加 &amp;wt_coupon=
                //    若未來 baseUrl 可能沒有 query，這裡也做保險處理
                var joiner = (baseUrl.indexOf(&#039;?&#039;) === -1) ? &#039;?&#039; : &#039;&amp;&#039;;
                return baseUrl + joiner + &#039;wt_coupon=&#039; + encodeURIComponent(coupon);
            }

            $(document).on(&#039;click&#039;, &#039;.copy-cart-link&#039;, function(e) {
                e.preventDefault();

                var button = $(this);
                var baseUrl = button.data(&#039;url&#039;);
                var originalHtml = button.html();

                if (button.hasClass(&#039;copying&#039;)) return;
                button.addClass(&#039;copying&#039;);

                var finalUrl = buildFinalUrl(baseUrl);

                // 若你希望「按取消就不複製」，把 buildFinalUrl 的取消行為改成 return null
                // 然後這裡加上：
                // if (!finalUrl) { button.removeClass(&#039;copying&#039;); return; }

                if (navigator.clipboard &amp;&amp; window.isSecureContext) {
                    navigator.clipboard.writeText(finalUrl).then(function() {
                        showCopySuccess(button, originalHtml);
                    }).catch(function(err) {
                        console.error(&#039;Copy failed: &#039;, err);
                        fallbackCopyTextToClipboard(finalUrl, button, originalHtml);
                    });
                } else {
                    fallbackCopyTextToClipboard(finalUrl, button, originalHtml);
                }
            });

            function fallbackCopyTextToClipboard(text, button, originalHtml) {
                var textArea = document.createElement(&quot;textarea&quot;);
                textArea.value = text;

                textArea.style.top = &quot;0&quot;;
                textArea.style.left = &quot;0&quot;;
                textArea.style.position = &quot;fixed&quot;;
                textArea.style.opacity = &quot;0&quot;;
                textArea.style.pointerEvents = &quot;none&quot;;

                document.body.appendChild(textArea);
                textArea.focus();
                textArea.select();

                try {
                    var successful = document.execCommand(&#039;copy&#039;);
                    if (successful) {
                        showCopySuccess(button, originalHtml);
                    } else {
                        showCopyError(button, originalHtml, text);
                    }
                } catch (err) {
                    console.error(&#039;Copy failed: &#039;, err);
                    showCopyError(button, originalHtml, text);
                }

                document.body.removeChild(textArea);
            }

            function showCopySuccess(button, originalHtml) {
                button.removeClass(&#039;copying&#039;).addClass(&#039;copied&#039;);
                button.html(&#039;&lt;span class=&quot;dashicons dashicons-yes&quot;&gt;&lt;/span&gt; 已複製&#039;);

                setTimeout(function() {
                    button.removeClass(&#039;copied&#039;);
                    button.html(originalHtml);
                }, 2500);
            }

            function showCopyError(button, originalHtml, text) {
                button.removeClass(&#039;copying&#039;);

                var tooltip = $(&#039;&lt;div class=&quot;copy-error-tooltip&quot; style=&quot;position:absolute;background:#333;color:#fff;padding:8px 12px;border-radius:4px;font-size:12px;z-index:9999;max-width:320px;word-break:break-all;&quot;&gt;複製失敗，請手動複製：&lt;br&gt;&#039; + text + &#039;&lt;/div&gt;&#039;);
                $(&#039;body&#039;).append(tooltip);

                var buttonOffset = button.offset();
                tooltip.css({
                    top: buttonOffset.top - tooltip.outerHeight() - 5,
                    left: buttonOffset.left
                });

                setTimeout(function() { tooltip.remove(); }, 5000);

                tooltip.on(&#039;click&#039;, function() {
                    if (navigator.clipboard) navigator.clipboard.writeText(text);
                    $(this).remove();
                });
            }

        });
    &lt;/script&gt;
    &lt;?php
}

// 可選：添加快速動作連結（在商品名稱下方）
add_filter(&#039;post_row_actions&#039;, &#039;add_copy_link_quick_action&#039;, 10, 2);
function add_copy_link_quick_action($actions, $post) {
    if ($post-&gt;post_type === &#039;product&#039;) {
        $product_id = (int) $post-&gt;ID;

        $site_url = my_copy_cart_site_url();
        $add_to_cart_url = $site_url . &#039;/cart/?add-to-cart=&#039; . $product_id;

        $actions[&#039;copy_cart_link&#039;] = &#039;&lt;a href=&quot;#&quot; class=&quot;copy-cart-link&quot; data-url=&quot;&#039; . esc_attr($add_to_cart_url) . &#039;&quot; style=&quot;color:#0073aa;&quot;&gt;📋 複製購物車連結&lt;/a&gt;&#039;;
    }
    return $actions;
}
</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/woocommerce-copy-cart-link-coupon/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Blocksy 主題 WooCommerce 產品頁新增下載與預覽彈窗實作筆記</title>
		<link>https://piglife.tw/technical-notes/blocksy-woocommerce-download-preview-modal/</link>
					<comments>https://piglife.tw/technical-notes/blocksy-woocommerce-download-preview-modal/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Thu, 13 Feb 2025 17:14:35 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[blocksy]]></category>
		<category><![CDATA[download]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[modal]]></category>
		<category><![CDATA[preview]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://piglife.tw/?p=487</guid>

					<description><![CDATA[說明如何利用 Blocksy 主題的 WooCommerce Hook，在產品頁購物車按鈕下方新增彈...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WooCommerce 產品頁面中，若產品包含可下載或預覽的檔案，提供使用者直覺的預覽與下載介面能提升購買體驗。本文適合熟悉 WordPress 主題開發與 WooCommerce 的工程師或自學者，說明如何利用 Blocksy 主題的特定 Hook，在產品頁購物車按鈕下方新增一個彈窗，顯示預覽與下載連結。</p>
<h2 class="wp-block-heading">新增功能程式碼</h2>
<p>透過 <code>blocksy:woocommerce:product-single:add_to_cart:after</code> Action Hook，將彈窗 HTML 結構插入產品頁。以下為關鍵程式碼片段：</p>
<pre><code class="lang-php language-php php">add_action(&#039;blocksy:woocommerce:product-single:add_to_cart:after&#039;, function() {
  global $post;
  echo &#039;&lt;div class=&quot;ks_links_section&quot;&gt;&#039;;
  echo &#039;&lt;div class=&quot;datasheet_btn&quot;&gt;&lt;button id=&quot;ks_show_links_modal&quot;&gt;Datasheet &gt;&lt;/button&gt;&lt;/div&gt;&#039;;
  echo &#039;&lt;div id=&quot;ks_links_modal&quot; class=&quot;ks_modal&quot;&gt;&#039;;
  echo &#039;&lt;div class=&quot;ks_modal-content&quot;&gt;&#039;;
  echo &#039;&lt;span class=&quot;ks_close&quot;&gt;&amp;times;&lt;/span&gt;&#039;;

  // 預覽連結
  $preview_links = get_post_meta($post-&gt;ID, &#039;_preview_links&#039;, true);
  if (!empty($preview_links)) {
    echo &#039;&lt;table class=&quot;file-table&quot; border=&quot;0&quot; width=&quot;100%&quot;&gt;&#039;;
    foreach ($preview_links as $link) {
      echo &#039;
&lt;tr&gt;&#039;;
      echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;name&#039;]) . &#039;&lt;/td&gt;&#039;;
      echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;date&#039;]) . &#039;&lt;/td&gt;&#039;;
      echo &#039;
&lt;td&gt;&lt;a href=&quot;&#039; . esc_url($link[&#039;url&#039;]) . &#039;&quot; target=&quot;_blank&quot;&gt;Preview&lt;/a&gt;&lt;/td&gt;&#039;;
      echo &#039;&lt;/tr&gt;&#039;;
    }
    echo &#039;&lt;/table&gt;&#039;;
  } else {
    echo &#039;
&lt;p&gt;No preview files available.&lt;/p&gt;&#039;;
  }

  // 下載連結（限已購買且登入用戶）
  if (is_user_logged_in() &amp;&amp; wc_customer_bought_product(&#039;&#039;, get_current_user_id(), $post-&gt;ID)) {
    echo &#039;
&lt;h4&gt;Download file&lt;/h4&gt;&#039;;
    $download_links = get_post_meta($post-&gt;ID, &#039;_download_links&#039;, true);
    if (!empty($download_links)) {
      echo &#039;&lt;table class=&quot;file-table&quot; border=&quot;0&quot; width=&quot;100%&quot;&gt;&#039;;
      foreach ($download_links as $index =&gt; $link) {
        echo &#039;
&lt;tr&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;name&#039;]) . &#039;&lt;/td&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;date&#039;]) . &#039;&lt;/td&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039;;
        echo &quot;&lt;button class=&#039;download-btn&#039; data-product-id=&#039;{$post-&gt;ID}&#039; data-index=&#039;{$index}&#039; style=&#039;color: #0073aa; background: none; border: none; cursor: pointer;&#039;&gt;Download&lt;/button&gt;&quot;;
        echo &#039;&lt;/td&gt;&#039;;
        echo &#039;&lt;/tr&gt;&#039;;
      }
      echo &#039;&lt;/table&gt;&#039;;
    } else {
      echo &#039;
&lt;p&gt;No download links available.&lt;/p&gt;&#039;;
    }
  }

  echo &#039;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&#039;;
});</code></pre>
<p>此段程式碼會在產品頁購物車按鈕下方插入一個按鈕，點擊後彈出包含預覽與下載連結的彈窗。下載連結僅對已購買且登入的用戶顯示。</p>
<h2 class="wp-block-heading">新增前端樣式與行為</h2>
<p>為了讓彈窗視覺與互動更友善，需要加入 CSS 與 JavaScript。</p>
<h3 class="wp-block-heading">CSS &#8211; 彈窗樣式</h3>
<pre><code class="lang-css language-css css">.ks_modal {
  display: none;
  position: fixed;
  z-index: 1000;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
}

.ks_modal-content {
  background-color: #fff;
  padding: 20px;
  margin: 15% auto;
  width: 50%;
  border-radius: 10px;
}

.ks_close {
  float: right;
  font-size: 24px;
  cursor: pointer;
}</code></pre>
<h3 class="wp-block-heading">JavaScript &#8211; 彈窗開關控制</h3>
<pre><code class="lang-js language-js js">jQuery(document).ready(function($) {
  $(&#039;#ks_show_links_modal&#039;).on(&#039;click&#039;, function() {
    $(&#039;#ks_links_modal&#039;).fadeIn();
  });

  $(&#039;.ks_close&#039;).on(&#039;click&#039;, function() {
    $(&#039;#ks_links_modal&#039;).fadeOut();
  });
});</code></pre>
<p>這段 JS 透過 jQuery 綁定按鈕事件，控制彈窗的顯示與隱藏。</p>
<h2 class="wp-block-heading">測試與調整</h2>
<ol>
<li>確認產品頁購物車按鈕下方是否出現「Datasheet &gt;」按鈕。</li>
<li>點擊按鈕，檢查彈窗是否正常顯示預覽連結。</li>
<li>測試下載連結顯示條件：
<ul>
<li>未登入或未購買用戶不應看到下載連結。</li>
<li>已購買且登入用戶可看到並使用下載按鈕。</li>
</ul></li>
</ol>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-text language-text text">// 在 WooCommerce 產品頁購物車按鈕區塊的下方新增彈窗顯示下載和預覽連結
add_action(&#039;blocksy:woocommerce:product-single:add_to_cart:after&#039;, function() {
  global $post;
  echo &#039;&lt;div class=&quot;ks_links_section&quot;&gt;&#039;;
  echo &#039;&lt;div class=&quot;datasheet_btn&quot;&gt;&lt;button id=&quot;ks_show_links_modal&quot;&gt;Datasheet &gt;&lt;/button&gt;&lt;/div&gt;&#039;;
  echo &#039;&lt;div id=&quot;ks_links_modal&quot; class=&quot;ks_modal&quot;&gt;&#039;;
  echo &#039;&lt;div class=&quot;ks_modal-content&quot;&gt;&#039;;
  echo &#039;&lt;span class=&quot;ks_close&quot;&gt;&amp;times;&lt;/span&gt;&#039;;

  // 顯示預覽連結
  echo &#039;
&lt;h4&gt;Preview file&lt;/h4&gt;&#039;;
  $preview_links = get_post_meta($post-&gt;ID, &#039;_preview_links&#039;, true);
  if (!empty($preview_links)) {
    echo &#039;&lt;table class=&quot;file-table&quot; border=&quot;0&quot; width=&quot;100%&quot;&gt;&#039;;
    foreach ($preview_links as $link) {
      echo &#039;
&lt;tr&gt;&#039;;
      echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;name&#039;]) . &#039;&lt;/td&gt;&#039;;
      echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;date&#039;]) . &#039;&lt;/td&gt;&#039;;
      echo &#039;
&lt;td&gt;&lt;a href=&quot;&#039; . esc_url($link[&#039;url&#039;]) . &#039;&quot; target=&quot;_blank&quot;&gt;Preview&lt;/a&gt;&lt;/td&gt;&#039;;
      echo &#039;&lt;/tr&gt;&#039;;
    }
    echo &#039;&lt;/table&gt;&#039;;
  } else {
    echo &#039;
&lt;p&gt;No preview files available.&lt;/p&gt;&#039;;
  }

  // 顯示下載連結（僅限已購買並登入的用戶）
  if (is_user_logged_in() &amp;&amp; wc_customer_bought_product(&#039;&#039;, get_current_user_id(), $post-&gt;ID)) {
    echo &#039;
&lt;h4&gt;Download file&lt;/h4&gt;&#039;;
    $download_links = get_post_meta($post-&gt;ID, &#039;_download_links&#039;, true);
    if (!empty($download_links)) {
      echo &#039;&lt;table class=&quot;file-table&quot; border=&quot;0&quot; width=&quot;100%&quot;&gt;&#039;;
      foreach ($download_links as $index =&gt; $link) {
        echo &#039;
&lt;tr&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;name&#039;]) . &#039;&lt;/td&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039; . esc_html($link[&#039;date&#039;]) . &#039;&lt;/td&gt;&#039;;
        echo &#039;
&lt;td&gt;&#039;;
        echo &quot;&lt;button class=&#039;download-btn&#039; data-product-id=&#039;{$post-&gt;ID}&#039; data-index=&#039;{$index}&#039; style=&#039;color: #0073aa; background: none; border: none; cursor: pointer;&#039;&gt;Download&lt;/button&gt;&quot;;
        echo &#039;&lt;/td&gt;&#039;;
        echo &#039;&lt;/tr&gt;&#039;;
      }
      echo &#039;&lt;/table&gt;&#039;;
    } else {
      echo &#039;
&lt;p&gt;No download links available.&lt;/p&gt;&#039;;
    }
  }

  echo &#039;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&#039;;
});

/* 彈窗樣式 */
.ks_modal {
  display: none;
  position: fixed;
  z-index: 1000;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
}

.ks_modal-content {
  background-color: #fff;
  padding: 20px;
  margin: 15% auto;
  width: 50%;
  border-radius: 10px;
}

.ks_close {
  float: right;
  font-size: 24px;
  cursor: pointer;
}

// 控制彈窗開關
jQuery(document).ready(function($) {
  $(&#039;#ks_show_links_modal&#039;).on(&#039;click&#039;, function() {
    $(&#039;#ks_links_modal&#039;).fadeIn();
  });

  $(&#039;.ks_close&#039;).on(&#039;click&#039;, function() {
    $(&#039;#ks_links_modal&#039;).fadeOut();
  });
});</code></pre>
<h2 class="wp-block-heading">結論</h2>
<p>透過 Blocksy 主題提供的 Hook，結合 WooCommerce 產品頁面，我們能輕鬆新增一個彈窗介面，提供使用者預覽與下載檔案的功能，提升產品資訊的呈現與使用者體驗，特別適合販售技術文件或數位內容的商店。</p>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/blocksy-woocommerce-download-preview-modal/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WooCommerce 會員後台嵌入 Amelia 預約日曆與隱藏管理員選單實作筆記</title>
		<link>https://piglife.tw/technical-notes/woocommerce-amelia-calendar-iframe/</link>
					<comments>https://piglife.tw/technical-notes/woocommerce-amelia-calendar-iframe/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Thu, 06 Feb 2025 10:39:41 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[admin_head]]></category>
		<category><![CDATA[amelia]]></category>
		<category><![CDATA[iframe]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[會員後台]]></category>
		<category><![CDATA[選單擴充]]></category>
		<guid isPermaLink="false">https://piglife.tw/?p=484</guid>

					<description><![CDATA[介紹如何在 WooCommerce 會員後台新增 Amelia 預約日曆選單，並利用 admin_h...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WooCommerce 會員後台嵌入 Amelia 預約日曆，可以讓用戶直接查看與管理預約，提升使用體驗。但直接嵌入 WordPress 後台頁面時，會看到不必要的管理員選單與工具列，影響介面整潔度。本文將介紹如何利用 WordPress 的 <code>admin_head</code> 鉤子與 WooCommerce 的會員選單過濾器，達成隱藏管理員元素並新增「我的日曆」選單的功能。</p>
<h2 class="wp-block-heading">隱藏 WordPress 管理員選單與工具列</h2>
<p>當 iframe 載入 WordPress 後台 Amelia 預約頁面時，左側選單、頂部工具列等管理員元素會干擾介面。透過 <code>admin_head</code> 鉤子判斷 URL 是否帶有 <code>iframe=true</code> 參數，動態插入 CSS 隱藏這些元素，並調整內容區域寬度與頁面邊距。</p>
<p>關鍵程式碼片段：</p>
<pre><code class="lang-php language-php php">function hide_admin_elements_for_iframe() {
 if (isset($_GET[&#039;iframe&#039;]) &amp;&amp; $_GET[&#039;iframe&#039;] == &#039;true&#039;) {
  echo &#039;
&lt;style&gt;
   #adminmenumain, #wpadminbar, .update-nag, #screen-meta, #screen-options-link-wrap, #contextual-help-link-wrap {
    display: none !important;
   }
   #wpcontent, #wpfooter { margin-left: 0; }
   html.wp-toolbar { padding-top: 0 !important; }
   body { margin: 0; padding: 0; }
   #wpfooter { display: none !important; }
  &lt;/style&gt;&#039;;
 }
}
add_action(&#039;admin_head&#039;, &#039;hide_admin_elements_for_iframe&#039;);</code></pre>
<p>這段程式碼確保只有在 iframe 模式下才隱藏管理員介面元素，避免影響正常管理操作。</p>
<h2 class="wp-block-heading">在 WooCommerce 會員後台新增「我的日曆」選單</h2>
<p>為方便用戶直接在會員中心查看 Amelia 預約日曆，使用 <code>add_rewrite_endpoint</code> 註冊新端點 <code>ameliacustomercalendar</code>，並透過 <code>woocommerce_account_menu_items</code> 過濾器在「我的課程」後新增「我的日曆」選單。</p>
<p>點擊「我的日曆」時，利用 <code>woocommerce_account_ameliacustomercalendar_endpoint</code> 鉤子輸出 iframe，載入 Amelia 預約日曆頁面，並帶入 <code>iframe=true</code> 參數以隱藏管理員元素。</p>
<p>關鍵程式碼片段：</p>
<pre><code class="lang-php language-php php">function add_ameliacustomercalendar_to_myaccount_menu() {
 add_rewrite_endpoint(&#039;ameliacustomercalendar&#039;, EP_ROOT | EP_PAGES);

 add_filter(&#039;woocommerce_account_menu_items&#039;, function($menu_links) {
  $new_menu_links = [];
  foreach ($menu_links as $key =&gt; $label) {
   $new_menu_links[$key] = $label;
   if ($key === &#039;ameliacustomerpanel&#039;) {
    $new_menu_links[&#039;ameliacustomercalendar&#039;] = &#039;我的日曆&#039;;
   }
  }
  return $new_menu_links;
 });

 add_action(&#039;woocommerce_account_ameliacustomercalendar_endpoint&#039;, function() {
  echo &#039;&lt;iframe width=&quot;100%&quot; height=&quot;600&quot; src=&quot;/wp-admin/admin.php?page=wpamelia-calendar&amp;iframe=true&quot;&gt;&lt;/iframe&gt;&#039;;
 });
}
add_action(&#039;init&#039;, &#039;add_ameliacustomercalendar_to_myaccount_menu&#039;);</code></pre>
<h2 class="wp-block-heading">測試與驗證</h2>
<ol>
<li>在瀏覽器網址列加入 <code>?iframe=true</code>，確認 WordPress 管理員選單與工具列是否隱藏。</li>
<li>進入 WooCommerce 會員後台，確認「我的日曆」選單是否出現。</li>
<li>點擊「我的日曆」，檢查 iframe 是否正確載入 Amelia 預約日曆，且無多餘管理員元素。</li>
</ol>
<h2 class="wp-block-heading">延伸與常見坑</h2>
<ul>
<li>若使用快取插件，新增端點後可能需要刷新重寫規則（Permalinks）。</li>
<li>iframe 高度可依需求調整，避免出現滾動條。</li>
<li>確保 Amelia 預約系統權限設定允許會員查看。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-text language-text text">function hide_admin_elements_for_iframe() {
 if (isset($_GET[&#039;iframe&#039;]) &amp;&amp; $_GET[&#039;iframe&#039;] == &#039;true&#039;) {
  echo &#039;
&lt;style&gt;
   #adminmenumain, /* 左側選單 */
   #wpadminbar, /* 頂部管理工具列 */
   .update-nag, /* 更新通知 */
   #screen-meta, /* 屏幕選項 */
   #screen-options-link-wrap, /* 屏幕選項按鈕 */
   #contextual-help-link-wrap /* 說明按鈕 */
   {
    display: none !important;
   }
   #wpcontent, #wpfooter{
    margin-left: 0;
   }
   html.wp-toolbar {
    padding-top: 0 !important;
   }
   body {
    margin: 0;
    padding: 0;
   }
   #wpfooter{
    display: none !important;
   }
  &lt;/style&gt;&#039;;
 }
}
add_action(&#039;admin_head&#039;, &#039;hide_admin_elements_for_iframe&#039;);

function add_ameliacustomercalendar_to_myaccount_menu() {
 add_rewrite_endpoint(&#039;ameliacustomercalendar&#039;, EP_ROOT | EP_PAGES);

 add_filter(&#039;woocommerce_account_menu_items&#039;, function($menu_links) {
  $new_menu_links = [];
  foreach ($menu_links as $key =&gt; $label) {
   $new_menu_links[$key] = $label;
   if ($key === &#039;ameliacustomerpanel&#039;) {
    $new_menu_links[&#039;ameliacustomercalendar&#039;] = &#039;我的日曆&#039;;
   }
  }
  return $new_menu_links;
 });

 add_action(&#039;woocommerce_account_ameliacustomercalendar_endpoint&#039;, function() {
  echo &#039;&lt;iframe width=&quot;100%&quot; height=&quot;600&quot; src=&quot;/wp-admin/admin.php?page=wpamelia-calendar&amp;iframe=true&quot;&gt;&lt;/iframe&gt;&#039;;
 });
}
add_action(&#039;init&#039;, &#039;add_ameliacustomercalendar_to_myaccount_menu&#039;);</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/woocommerce-amelia-calendar-iframe/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>使用 PHP 記錄 WooCommerce 訂單更新資料結構技術筆記</title>
		<link>https://piglife.tw/technical-notes/php-log-woocommerce-order-data/</link>
					<comments>https://piglife.tw/technical-notes/php-log-woocommerce-order-data/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Thu, 09 Jan 2025 12:49:56 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[訂單資料]]></category>
		<category><![CDATA[除錯]]></category>
		<guid isPermaLink="false">https://piglife.tw/?p=468</guid>

					<description><![CDATA[說明如何使用 PHP 透過 WooCommerce 的訂單狀態變更鉤子，將訂單詳細資料以 JSON ...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在開發基於 WordPress 的 WooCommerce 網站時，了解訂單資料結構對於客製化功能非常重要。當訂單狀態或詳細資料更新時，能夠即時記錄這些資料有助於除錯與後續分析。本文針對 WooCommerce 訂單更新事件，說明如何使用 PHP 透過鉤子（hook）將訂單資料以 JSON 格式記錄到檔案中。</p>
<h2 class="wp-block-heading">需求情境</h2>
<ol>
<li>監聽 WooCommerce 訂單更新事件。</li>
<li>獲取訂單的完整詳細資料。</li>
<li>將資料儲存為 JSON 格式，方便後續分析或除錯。</li>
</ol>
<h2 class="wp-block-heading">解決方案</h2>
<p>利用 WooCommerce 提供的 <code>woocommerce_order_status_changed</code> 鉤子，在訂單狀態變更時觸發自訂函數，取得訂單資料並寫入檔案。</p>
<pre><code class="lang-php language-php php">add_action(&#039;woocommerce_order_status_changed&#039;, &#039;log_order_data&#039;, 10, 4);

function log_order_data($order_id, $old_status, $new_status, $order) {
    $data = [
        &#039;order_id&#039; =&gt; $order_id,
        &#039;old_status&#039; =&gt; $old_status,
        &#039;new_status&#039; =&gt; $new_status,
        &#039;order_data&#039; =&gt; $order-&gt;get_data() // 獲取訂單詳細資料
    ];

    write_to_log_file($data, &#039;order_update_log.json&#039;);
}

function write_to_log_file($data, $filename = &#039;meta_data.json&#039;) {
    $log_dir = __DIR__ . &#039;/logs&#039;; // 指定記錄檔存放目錄
    if (!file_exists($log_dir)) {
        mkdir($log_dir, 0755, true); // 如果目錄不存在，創建目錄
    }
    $log_file = $log_dir . &#039;/&#039; . $filename;

    // 將資料轉換為 JSON 格式
    $json_data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

    // 將 JSON 資料寫入文件（直接覆寫內容）
    file_put_contents($log_file, $json_data);
}</code></pre>
<h2 class="wp-block-heading">程式碼解說</h2>
<ol>
<li>
<p><strong>監聽訂單狀態更新事件</strong>
使用 WooCommerce 的 <code>woocommerce_order_status_changed</code> 鉤子，當訂單狀態變更時觸發 <code>log_order_data</code> 函數。</p>
<pre><code class="lang-php language-php php">add_action(&#039;woocommerce_order_status_changed&#039;, &#039;log_order_data&#039;, 10, 4);</code></pre>
</li>
<li>
<p><strong>獲取訂單詳細資料</strong>
透過 <code>WC_Order</code> 物件的 <code>get_data()</code> 方法取得完整訂單資料，包含客戶資訊、商品清單、付款方式等。</p>
<pre><code class="lang-php language-php php">&#039;order_data&#039; =&gt; $order-&gt;get_data()</code></pre>
</li>
<li>
<p><strong>記錄資料至 JSON 檔案</strong>
將資料轉換為 JSON 格式，並寫入指定的 <code>logs</code> 目錄下的檔案中，若目錄不存在則自動建立。</p>
<pre><code class="lang-php language-php php">write_to_log_file($data, &#039;order_update_log.json&#039;);</code></pre>
</li>
</ol>
<h2 class="wp-block-heading">範例輸出</h2>
<p>假設訂單狀態從 <code>processing</code> 更新為 <code>completed</code>，記錄的 JSON 資料範例如下：</p>
<pre><code class="lang-json language-json json">{
  &quot;order_id&quot;: 1234,
  &quot;old_status&quot;: &quot;processing&quot;,
  &quot;new_status&quot;: &quot;completed&quot;,
  &quot;order_data&quot;: {
    &quot;id&quot;: 1234,
    &quot;status&quot;: &quot;completed&quot;,
    &quot;currency&quot;: &quot;USD&quot;,
    &quot;total&quot;: &quot;99.99&quot;,
    &quot;customer_id&quot;: 5678,
    &quot;billing&quot;: {
      &quot;first_name&quot;: &quot;John&quot;,
      &quot;last_name&quot;: &quot;Doe&quot;,
      &quot;email&quot;: &quot;john.doe@example.com&quot;
    },
    &quot;line_items&quot;: [
      {
        &quot;name&quot;: &quot;T-Shirt&quot;,
        &quot;quantity&quot;: 2,
        &quot;total&quot;: &quot;49.99&quot;
      }
    ]
  }
}</code></pre>
<h2 class="wp-block-heading">適用場景</h2>
<ul>
<li><strong>除錯需求</strong>：快速了解 WooCommerce 訂單資料結構，協助定位問題。</li>
<li><strong>數據分析</strong>：將記錄的 JSON 資料用於訂單數據分析。</li>
<li><strong>客製化功能開發</strong>：根據訂單詳細資料設計特定邏輯。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-text language-text text">add_action(&#039;woocommerce_order_status_changed&#039;, &#039;log_order_data&#039;, 10, 4);

function log_order_data($order_id, $old_status, $new_status, $order) {
    $data = [
        &#039;order_id&#039; =&gt; $order_id,
        &#039;old_status&#039; =&gt; $old_status,
        &#039;new_status&#039; =&gt; $new_status,
        &#039;order_data&#039; =&gt; $order-&gt;get_data() // 獲取訂單詳細資料
    ];

    write_to_log_file($data, &#039;order_update_log.json&#039;);
}

function write_to_log_file($data, $filename = &#039;meta_data.json&#039;) {
    $log_dir = __DIR__ . &#039;/logs&#039;; // 指定記錄檔存放目錄
    if (!file_exists($log_dir)) {
        mkdir($log_dir, 0755, true); // 如果目錄不存在，創建目錄
    }
    $log_file = $log_dir . &#039;/&#039; . $filename;

    // 將資料轉換為 JSON 格式
    $json_data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

    // 將 JSON 資料寫入文件（直接覆寫內容）
    file_put_contents($log_file, $json_data);
}</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/php-log-woocommerce-order-data/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>利用 WordPress 實現 WooCommerce 與 Ragic 訂單資料自動同步技術筆記</title>
		<link>https://piglife.tw/technical-notes/woocommerce-ragic-data-sync/</link>
					<comments>https://piglife.tw/technical-notes/woocommerce-ragic-data-sync/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Tue, 31 Dec 2024 03:19:23 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[api同步]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ragic]]></category>
		<category><![CDATA[woocommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[訂單管理]]></category>
		<category><![CDATA[資料整合]]></category>
		<guid isPermaLink="false">https://piglife.tw/?p=438</guid>

					<description><![CDATA[說明如何利用 WordPress 與 WooCommerce 將訂單資料自動同步至 Ragic 雲端...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在電子商務系統中，訂單資料的管理與整合是提升營運效率的重要環節。WooCommerce 作為 WordPress 上熱門的電商插件，能有效管理訂單，但若要進一步分析與整合資料，將訂單同步至 Ragic 雲端資料庫是一個實用方案。本文適合具備基礎 PHP 與 WordPress 開發經驗的工程師或自學者，說明如何自動將 WooCommerce 訂單資料同步至 Ragic。</p>
<h2 class="wp-block-heading">為什麼要同步 WooCommerce 訂單至 Ragic？</h2>
<p>Ragic 是一款靈活的雲端資料庫工具，方便用戶集中管理與分析業務資料。透過自動同步 WooCommerce 訂單資料，可以：</p>
<ul>
<li>減少手動輸入，提升工作效率</li>
<li>降低人為錯誤，確保資料正確性</li>
<li>集中管理訂單數據，方便後續分析與決策</li>
</ul>
<h2 class="wp-block-heading">教學步驟</h2>
<h3 class="wp-block-heading">1. 啟用訂單完成後的同步觸發</h3>
<p>利用 WooCommerce 的鉤子 <code>woocommerce_order_status_completed</code>，當訂單狀態變更為「已完成」時觸發同步函式：</p>
<pre><code class="lang-php language-php php">add_action(&#039;woocommerce_order_status_completed&#039;, &#039;sync_order_to_ragic&#039;);</code></pre>
<h3 class="wp-block-heading">2. 擷取 WooCommerce 訂單資料</h3>
<p>使用 WooCommerce API 取得訂單詳細資訊，包括用戶姓名、電話、地址及訂單商品：</p>
<pre><code class="lang-php language-php php">$order = wc_get_order($order_id);
$email = $order-&gt;get_billing_email();
$first_name = $order-&gt;get_billing_first_name();
$last_name = $order-&gt;get_billing_last_name();
$phone = $order-&gt;get_billing_phone();
$address = $order-&gt;get_billing_address_1() . &#039;, &#039; . $order-&gt;get_billing_city() . &#039;, &#039; . $order-&gt;get_billing_state() . &#039;, &#039; . $order-&gt;get_billing_postcode();</code></pre>
<h3 class="wp-block-heading">3. 整理訂單商品資料格式</h3>
<p>將訂單中每項商品名稱、數量與價格組合成字串，方便傳輸：</p>
<pre><code class="lang-php language-php php">$items = [];
foreach ($order-&gt;get_items() as $item) {
  $items[] = $item-&gt;get_name() . &#039; x &#039; . $item-&gt;get_quantity() . &#039; (&#039; . $item-&gt;get_total() . &#039;)&#039;;
}
$items_string = implode(&#039;; &#039;, $items);</code></pre>
<h3 class="wp-block-heading">4. 組織符合 Ragic API 欄位結構的資料</h3>
<p>根據 Ragic 表單欄位 ID，將資料映射成陣列格式：</p>
<pre><code class="lang-php language-php php">$ragic_data = [
  &#039;1000686&#039; =&gt; &#039;網站&#039;,
  &#039;1000595&#039; =&gt; $first_name . &#039; &#039; . $last_name,
  &#039;1000596&#039; =&gt; $phone,
  &#039;1000563&#039; =&gt; $address,
  &#039;1000634&#039; =&gt; $email,
  &#039;1000597&#039; =&gt; $items_string,
  &#039;1000610&#039; =&gt; $order-&gt;get_total(),
  &#039;1000594&#039; =&gt; $order-&gt;get_date_created()-&gt;date(&#039;Y-m-d H:i:s&#039;),
];</code></pre>
<h3 class="wp-block-heading">5. 使用 Ragic API 同步資料</h3>
<p>透過 <code>wp_remote_post</code> 發送 POST 請求，將資料以 JSON 格式送至 Ragic：</p>
<pre><code class="lang-php language-php php">$response_ragic = wp_remote_post(
  $ragic_url,
  [
    &#039;method&#039; =&gt; &#039;POST&#039;,
    &#039;headers&#039; =&gt; [
      &#039;Content-Type&#039; =&gt; &#039;application/json&#039;,
      &#039;Authorization&#039; =&gt; &#039;Basic YOUR_RAGIC_API_CREDENTIAL&#039;,
    ],
    &#039;body&#039; =&gt; json_encode($ragic_data),
  ]
);</code></pre>
<h3 class="wp-block-heading">6. 檢查 API 回應並記錄錯誤</h3>
<p>確保同步成功，並在失敗時記錄錯誤訊息以利除錯：</p>
<pre><code class="lang-php language-php php">if (is_wp_error($response_ragic)) {
  error_log(&#039;Ragic 同步失敗：&#039; . $response_ragic-&gt;get_error_message());
} else {
  $status_code_ragic = wp_remote_retrieve_response_code($response_ragic);
  $body_ragic = wp_remote_retrieve_body($response_ragic);

  if ($status_code_ragic == 200 || $status_code_ragic == 201) {
    error_log(&#039;Ragic 同步成功：&#039; . $body_ragic);
  } else {
    error_log(&quot;Ragic API 回應錯誤 (狀態碼: $status_code_ragic)：$body_ragic&quot;);
  }
}</code></pre>
<h2 class="wp-block-heading">注意事項</h2>
<ol>
<li><strong>API Key 安全性</strong>：避免將 API Key 明文寫入程式碼，建議使用環境變數或安全存取方式。</li>
<li><strong>欄位對應檢查</strong>：確認 Ragic 表單欄位 ID 與程式碼中對應正確，避免資料錯置。</li>
<li><strong>測試與效能優化</strong>：正式上線前充分測試同步流程，確保資料完整且效能穩定。</li>
</ol>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-text language-text text">add_action(&#039;woocommerce_order_status_completed&#039;, &#039;sync_order_to_ragic&#039;);

function sync_order_to_ragic($order_id) {
  $order = wc_get_order($order_id);
  $email = $order-&gt;get_billing_email();
  $first_name = $order-&gt;get_billing_first_name();
  $last_name = $order-&gt;get_billing_last_name();
  $phone = $order-&gt;get_billing_phone();
  $address = $order-&gt;get_billing_address_1() . &#039;, &#039; . $order-&gt;get_billing_city() . &#039;, &#039; . $order-&gt;get_billing_state() . &#039;, &#039; . $order-&gt;get_billing_postcode();

  $items = [];
  foreach ($order-&gt;get_items() as $item) {
    $items[] = $item-&gt;get_name() . &#039; x &#039; . $item-&gt;get_quantity() . &#039; (&#039; . $item-&gt;get_total() . &#039;)&#039;;
  }
  $items_string = implode(&#039;; &#039;, $items);

  $ragic_data = [
    &#039;1000686&#039; =&gt; &#039;網站&#039;,
    &#039;1000595&#039; =&gt; $first_name . &#039; &#039; . $last_name,
    &#039;1000596&#039; =&gt; $phone,
    &#039;1000563&#039; =&gt; $address,
    &#039;1000634&#039; =&gt; $email,
    &#039;1000597&#039; =&gt; $items_string,
    &#039;1000610&#039; =&gt; $order-&gt;get_total(),
    &#039;1000594&#039; =&gt; $order-&gt;get_date_created()-&gt;date(&#039;Y-m-d H:i:s&#039;),
  ];

  $ragic_url = &#039;https://www.ragic.com/your_ragic_url&#039;; // 請替換為實際 Ragic API URL

  $response_ragic = wp_remote_post(
    $ragic_url,
    [
      &#039;method&#039; =&gt; &#039;POST&#039;,
      &#039;headers&#039; =&gt; [
        &#039;Content-Type&#039; =&gt; &#039;application/json&#039;,
        &#039;Authorization&#039; =&gt; &#039;Basic YOUR_RAGIC_API_CREDENTIAL&#039;, // 請替換為實際授權資訊
      ],
      &#039;body&#039; =&gt; json_encode($ragic_data),
    ]
  );

  if (is_wp_error($response_ragic)) {
    error_log(&#039;Ragic 同步失敗：&#039; . $response_ragic-&gt;get_error_message());
  } else {
    $status_code_ragic = wp_remote_retrieve_response_code($response_ragic);
    $body_ragic = wp_remote_retrieve_body($response_ragic);

    if ($status_code_ragic == 200 || $status_code_ragic == 201) {
      error_log(&#039;Ragic 同步成功：&#039; . $body_ragic);
    } else {
      error_log(&quot;Ragic API 回應錯誤 (狀態碼: $status_code_ragic)：$body_ragic&quot;);
    }
  }
}</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/woocommerce-ragic-data-sync/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
