Select Page
如何 Debug WooCommerce 3 +

如何 Debug WooCommerce 3 +

woocommerce 在 wordpress 中是可以擁有自己的除錯系統的,可以獨立在 wordpress, php, nginx 以外,可以利用 WooCommerce 官方提供的 Log 記錄功能,可以記錄各種狀態,和自己的標籤,可以比較有秩序且快速的除錯

使用 WC_Logger

WC_Logger官方文件示範了標準用法

$log = new WC_Logger();
$log_entry = print_r( $e, true );
$log_entry .= 'Exception Trace: ' . print_r( $e->getTraceAsString(), true );
$log->log( 'new-woocommerce-log-name', $log_entry );

簡易用法

$logger = wc_get_logger();
$logger->debug( 'debug message', array( 'source' => 'my-extension' ) );

查看 WC Log

在 WP 後台,選擇 WooCommerce -> 狀態

並且選擇 狀態 -> 日誌紀錄 ,選擇完畢後,在選擇你建立的紀錄標籤後,就可以看到清楚的訊息輸出了

同廠加映,用WordPress內建的除錯

編輯 wp-config,將 WP_DEBUG 的選項打開,缺點就是訊息太多,比較不好看清楚

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
wordpress 優化 PHP : WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers)

wordpress 優化 PHP : WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers)

我的系統是 Ubuntu 20 + Nginx + Php7.4-fpm ,遇到不能安裝程式,安裝到一半都會停掉,去檢查 /var/log/php7.4fpm.log ,發現出現 warning message

warning : [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 15 total children

我的解決方法是去到 /etc/php/7.4/fpm/pool.d 下打開 www.conf

sudo nano /etc/php/7.4/fpm/pool.d/www.conf

然後改善 pm 的相關設定值,以我的機器為例,16GB RAM,CPU 4 顆,我的設定值參考公式為

pm.start_servers = min_spare_servers + (max_spare_servers - min_spare_servers) / 2

所以我修改的參數如下

pm = dynamic
pm.max_children = 30 # 10-30 間是常見的設定
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 10
pm.max_requests = 5000

修改完畢之後,重啟就不會看到問題了

pm.max_children 的合理配置方法

要合理的評估一個數字,要先考量以下幾點

  1. 伺服器可用的記憶體(RAM):確認伺服器總共有多少記憶體可供 PHP-FPM 使用。
  2. 每個 PHP 進程的平均記憶體消耗量:觀察或測試你的應用,以確定每個 PHP 進程大概需要多少記憶體。
  3. 保留記憶體:為作業系統和其他服務保留一部分記憶體,以確保系統穩定運行。

步驟 1:測量 PHP 進程的平均記憶體消耗

ps -ylC php-fpm8.2 --sort:rss

找到 RSS 列的值(常駐集大小),將它們加起來,然後除以 PHP-FPM 進程的數量,得到平均記憶體消耗量(單位是 KB)。

步驟 2:計算可用記憶體

確定伺服器上為 PHP-FPM 分配的記憶體總量。如果伺服器僅運行 PHP-FPM(例如,沒有資料庫等其他服務),則可以減去為作業系統和其他必要服務保留的記憶體量。一個常見的保留量是 512MB 到 1GB。

步驟 3:計算 pm.max_children

利用以下公式

pm.max_children = (伺服器可用記憶體 – 保留記憶體) / 平均每個 PHP 進程的記憶體消耗

假設:

  • 伺服器有 6GB(6144MB)的 RAM。
  • 作業系統和其他服務保留了 1GB(1024MB)。
  • 通過測量,你發現每個 PHP 進程平均消耗 50MB 記憶體

pm.max_children = (6144MB – 1024MB) / 50MB = 102.4

在這個例子中,你可以將 pm.max_children 設置為大約 100。

注意:始終留有足夠的餘地來處理意外的高峰,不要將所有資源都分配給 PHP-FPM,以避免伺服器過載。此外,實際部署後,應監控伺服器性能,並根據需要調整 pm.max_children 的值。

參考資料如下

https://www.gushiciku.cn/pl/p9Vf/zh-tw

Woocommerce 在分類目錄中隱藏無庫存的商品

網路上有很多解決在 woocommerce 的分類目錄中,隱藏無庫存商品的方法,但都不合我用,只好自己動手寫一個適合的,設計的思維是,在取得商品的之前 (pre_get_posts) ,我們判斷商品是否已經無庫存,是的話,我們就把發布狀態設定為私密 (private),型錄可見度設定為「隱藏」

在 functions.php 中加入以下的程式碼

// 在目錄中隱藏無庫存的商品
add_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
function custom_pre_get_posts_query( $q ) {
if ( ! $q->is_main_query() ) return;
if ( ! $q->is_post_type_archive() ) return;
if ( ! is_admin() ) {

$q->set( 'tax_query', array(array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => array( 'sb00001' ), // 設定在那幾個目錄中要處理,多個目錄的話用逗號分隔
'operator' => 'NOT IN',
'post_status' => 'private',
)));
}
remove_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
}

2022-03-28 更新

上面的寫法不一定成功,補充另一個寫法,只要在商品庫存量被變更的時候,去將商品的顯示狀態設定成隱藏起來就可以了

add_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
function custom_pre_get_posts_query( $q ) {
if ( ! $q->is_main_query() ) return;
if ( ! $q->is_post_type_archive() ) return;
if ( ! is_admin() ) {
	
global $wpdb;
$results = $wpdb->get_results(
'SELECT * FROM `wp_aws_index` c WHERE c.id in (SELECT a.ID FROM `wp_posts` a
join `wp_postmeta` b ON a.ID = b.post_id 
where b.meta_key = "_stock_status" and b.meta_value = "outofstock");

UPDATE `wp_aws_index` c SET c.visibility = "hidden" WHERE c.id in 
(SELECT a.ID FROM `wp_posts` a join `wp_postmeta` b ON a.ID = b.post_id where b.meta_key = "_stock_status" and b.meta_value = "outofstock");

DELETE FROM `wp_aws_cache`;
delete from `wp_term_relationships` WHERE `term_taxonomy_id` = 7 AND `object_id` in (SELECT a.ID FROM `wp_posts` a join `wp_postmeta` b ON a.ID = b.post_id where b.meta_key = "_stock_status" and b.meta_value = "outofstock");
INSERT INTO `wp_term_relationships` (`object_id`, `term_taxonomy_id`) VALUES ((SELECT a.ID FROM `wp_posts` a join `wp_postmeta` b ON a.ID = b.post_id where b.meta_key = "_stock_status" and b.meta_value = "outofstock"), 7);'
, OBJECT);

remove_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
}
WordPress-簡潔又好用的預約系統-BookingPress

WordPress-簡潔又好用的預約系統-BookingPress

預約系統在各行各業中都會用到,預約醫生、老師時間,預約健身房時間,預約會議室時間,每種需要都不一樣,也很複雜,這邊先推薦一個可以用在預約專業人員時間的預約系統,使用起來,對於他的設定介面我覺得是預約系統中設計得最好的,80%的功能都可以直接使用。

BookingPress 最喜歡的功能如下

  • 設定介面簡潔好用
  • 使用者前端界面好看清楚
  • 可以快速自訂顯示文字,包含中文化
  • 使用者端有精靈,可以協助預定時間
  • 可以預約加上線上付款
  • 預約時間後,可以由後台再去確認
  • 時間可以切分到分鐘

使用 BookingPress 提供預約服務

1.先建立一個分類目錄 Categories

建立分類目錄的目的是用來放服務 Service

2.建立服務

在剛剛建立的分類下,新增服務,服務可以是某一位醫生、老師

3.新增頁面,用來放置預約表單

在頁面中,新增一個區塊並且插入 [bookingpress_form] ,這個區塊未來會是一個精靈,引導使用者去預約

前端的頁面呈現的樣子

4.設定頁面呈現資料的細節

接下來主要去 bookingpress -> customize 中修改顯示的資訊,這邊可以更改成中文,和需要顯示的資訊

5.設定功能細節

在 bookingpress -> setting 中,每一個選項都還蠻重要的,都要進去做些微調,其中 DaysOff 比較難懂一點,一般的預約系統是正向表列,bookingpress 則是負向表列,在這個表中,有選擇的,就會是假日檔,也就是那天就不開放預約,,其他的時間會去跟你的工作時間中比對後在前端顯示給使用者,並且這個設定是全域的,沒法針對單一的服務

延伸閱讀以及下載

bookingpress 官網 https://www.bookingpressplugin.com/

bookingpress plug 下載 https://tw.wordpress.org/plugins/bookingpress-appointment-booking/

Woocommerce 點選圖片就可以直接加入購物車或是到結帳頁面

點選商品圖片後的行為網址

客戶常常是想要快速結帳,所以通常希望在商品頁面中,點選圖片就可以直接進入結帳了,這時候在 wordpress woocommerce 中沒有特別好用的 Pugin 可以利用,我的解法是在佈景主題中的 functions.php ,加入下面的程式碼片段,其中add-to-cart=id , id 指的是商品 id , quantity=1,1 是數量。

  • 點選圖片後,會直接到結帳頁面,/checkout/?add-to-cart=’.$product->get_id().’quantity=1
  • 點選圖片後,會直接到購物車,/cart/?add-to-cart=’.$product->get_id().’&quantity=1
  • /?add-to-cart=’.$product->get_id()

functions.php

// woocommerce
// 設定圖片上的商品連結
if ( ! function_exists( 'woocommerce_template_loop_product_link_open' ) ) {
	/**
	 * Insert the opening anchor tag for products in the loop.
	 */
	function woocommerce_template_loop_product_link_open() {
		global $product;
		
        //原先的設定
		//$link = apply_filters( 'woocommerce_loop_product_link', get_the_permalink(), $product );
      
      	// 1.改成直接到結帳頁面中
        $link = '/checkout/?add-to-cart='.$product->get_id().'&quantity=1';
      	
      	// 2.點選圖片直接會到購物車中
      	$link = '/cart/?add-to-cart='.$product->get_id().'&quantity=1';
      
      	// 3.點選圖片後不跳轉,但會加入到購物車中
      	$link = '/?add-to-cart='.$product->get_id();

		echo '<a href="' . esc_url( $link ) . '" class="woocommerce-LoopProduct-link woocommerce-loop-product__link">';
	}
}

搜尋商品後,點選產品清單中的圖片直接進入購物車

/wp-content/themes/themename/template-tags.php

修改$permalink = !empty( $args[‘permalink’] ) ? $args[‘permalink’] : ‘/checkout/?add-to-cart=’.$post_id.’quantity=1′;

function et_extra_get_post_thumb( $args = array() ) {
	$default_args = array(
		'post_id'                    => 0,
		'size'                       => '',
		'height'                     => 50,
		'width'                      => 50,
		'title'                      => '',
		'link_wrapped'               => true,
		'permalink'                  => '',
		'a_class'                    => array(),
		'img_class'                  => array(),
		'img_style'                  => '',
		'img_after'                  => '', // Note: this value is not escaped/sanitized, and should be used for internal purposes only, not any user input
		'post_format_thumb_fallback' => false,
		'fallback'                   => '',
		'thumb_src'                  => '',
		'return'                     => 'img',
	);

	$args = wp_parse_args( $args, $default_args );

	$post_id = $args['post_id'] ? $args['post_id'] : get_the_ID();

	// add to cart link
	//$permalink = !empty( $args['permalink'] ) ? $args['permalink'] : get_the_permalink( $post_id );
	$permalink = !empty( $args['permalink'] ) ? $args['permalink'] : '/checkout/?add-to-cart='.$post_id.'quantity=1';
	
	$title = !empty( $args['title'] ) ? $args['title'] : get_the_title( $post_id );

	$width = (int) apply_filters( 'et_extra_post_thumbnail_width', $args['width'] );
	$height = (int) apply_filters( 'et_extra_post_thumbnail_height', $args['height'] );
	$size = !empty( $args['size'] ) ? $args['size'] : array( $width, $height );
	$thumb_src = $args['thumb_src'];
	$img_style = $args['img_style'];

	$thumbnail_id = get_post_thumbnail_id( $post_id );

	if ( !$thumbnail_id && !$args['thumb_src'] ) {
		if ( $args['post_format_thumb_fallback'] ) {
			$post_format = et_get_post_format();
			if ( in_array( $post_format, array( 'video', 'quote', 'link', 'audio', 'map', 'text' ) ) ) {
				$thumb_src = et_get_post_format_thumb( $post_format, 'thumb' );
			} else {
				$thumb_src = et_get_post_format_thumb( 'text', 'thumb' );
			}
		} else if ( !empty( $args['fallback'] ) ) {
			return $args['fallback'];
		} else {
			$thumb_src = et_get_post_format_thumb( 'text', 'icon' );
		}
	}

	if ( $thumbnail_id ) {
		list($thumb_src, $thumb_width, $thumb_height) = wp_get_attachment_image_src( $thumbnail_id, $size );
	}

	if ( 'thumb_src' === $args['return'] ) {
		return $thumb_src;
	}

	$image_output = sprintf(
		'<img src="%1$s" alt="%2$s"%3$s %4$s/>%5$s',
		esc_attr( $thumb_src ),
		esc_attr( $title ),
		( !empty( $args['img_class'] ) ? sprintf( ' class="%s"', esc_attr( implode( ' ', $args['img_class'] ) ) ) : '' ),
		( !empty( $img_style ) ? sprintf( ' style="%s"', esc_attr( $img_style ) ) : '' ),
		$args['img_after']
	);

	if ( $args['link_wrapped'] ) {
		$image_output = sprintf(
			'<a href="%1$s" title="%2$s"%3$s%5$s>
				%4$s
			</a>',
			esc_attr( $permalink ),
			esc_attr( $title ),
			( !empty( $args['a_class'] ) ? sprintf( ' class="%s"', esc_attr( implode( ' ', $args['a_class'] ) ) ) : '' ),
			$image_output,
			( !empty( $img_style ) ? sprintf( ' style="%s"', esc_attr( $img_style ) ) : '' )
		);
	}

	return $image_output;
}