WooCommerce

写代码定制WooCommerce产品页模板(2021)

定制WooCommerce产品页模板的方法有很多,最常见的是用插件或主题自带的功能,例如flatsome主题就支持用拖拽的方式定制产品模板,且能给每个产品使用不同的模板,有兴趣的可以自行研究,这里就不展开描述了。本文要介绍的是写代码定制的方法,灵活省事。

定制WooCommerce产品页模板用到的钩子

产品页相关的模板主要有single-product.phpcontent-single-product.php,我们用代码把模板里主要的action和挂载到action上的function都显示出来,用默认主题twenty twenty-one来测试,如下图所示,函数名称后面的数字是优先级,action控制元素的位置,而优先级可以决定同一位置的元素的显示的顺序。

定制WooCommerce产品页模板用到的钩子

hooks.wpdesk.org这个网站可以查看更详细的钩子位置。

如果你喜欢用插件,Show Hooks可以很方便的查看当前页面的action设filter。

通过调整模块位置定制模板

标题、价格、gallery等这些元素是卡片,action是卡槽,先把想重排的卡片从卡槽里拿出来,再按照希望的顺序重新放回去。首先,用remove_all_actions函数把卡片拿出来,注释掉的是不想重设的actions。

/**
 * Remove the default content of the single product template
 */
add_action( 'template_redirect', 'sola_reset_product_actions' );
function sola_reset_product_actions(){
	$hooks = array(
	    //'woocommerce_before_main_content',
	    'woocommerce_after_main_content',
	    'woocommerce_sidebar',
	    //'woocommerce_before_single_product',
	    'woocommerce_before_single_product_summary',
	    'woocommerce_single_product_summary',
	    'woocommerce_after_single_product_summary',
	    'woocommerce_after_single_product',
	);

	foreach( $hooks as $hook ){
	    remove_all_actions( $hook );
	}
}

重新放到自己希望的位置。

add_action( 'template_redirect', 'sola_custom_single_product_template' );
function sola_custom_single_product_template(){
	$elements = array(
		'woocommerce_before_single_product_summary' => array(
			'woocommerce_show_product_sale_flash',
			'woocommerce_show_product_images',
			'woocommerce_template_single_sharing',
			'woocommerce_template_single_title',
			'woocommerce_template_single_price',
			'woocommerce_template_single_add_to_cart',
			'woocommerce_template_single_rating',
			'woocommerce_template_single_meta',
			'woocommerce_output_product_data_tabs'
		),
		'woocommerce_after_single_product_summary' => array(
			'woocommerce_upsell_display',
			'woocommerce_output_related_products'
		),
	);

	foreach( $elements as $action => $functions ){
		
		$priority = 10;

		foreach( $functions as $function ){
			add_action( $action, $function, $priority );
			$priority += 10;
		}	
	}
}

再来查看一下action上的元素,已经变成程序里指定的样子,如下图所示,因为没有写css美化,样子就凑合看吧。

修改后的actions

附:查看action位置使用的代码

class Sola_Show_Hook_Position{

	public $included_hooks  = false;

	public $excluded_hooks  = false;

	public $hook_has_string = '';

	public $after_action    = '';


	public function run(){

		if( is_admin() ){
			return;
		}
		
		add_filter( 'all', [$this, 'hook_into_all_actions'] );
	}

	function hook_into_all_actions( $hook ){

		global $wp_actions;

		if( !isset( $wp_actions[$hook]) ){
			return;
		}

		if( $this->hook_has_string && strpos($hook, $this->hook_has_string) === false ){
			return;
		}

		if( is_array($this->included_hooks) && sizeof($this->included_hooks) && in_array($hook, $this->included_hooks) === false ){
			return;
		}

		if( is_array($this->excluded_hooks) && sizeof($this->excluded_hooks) && in_array($hook, $this->excluded_hooks) !== false) {
			return;
		}

		if ( $this->after_action && !did_action($this->after_action) ){
			return;
		}

		$this->show_position( $hook );
	}

	function show_position( $hook ){

		echo '<div style="background:#eff8fe;padding:2rem;font-size:1.2rem;margin:1rem 0;clear:both;color:#0075dd">
		<h3 style="color:#0075dd;font-weight:bold;line-height:1.2">',
		$hook,
		'</h3>',
		$this->render_callbacks($hook),
		'</div>';
	}

	function render_callbacks( $hook ){

		global $wp_filter;

		if( empty($hook) || !isset($wp_filter[$hook]) ){
			return;
		}

		$actions = $wp_filter[$hook]->callbacks;

		foreach( $actions as $priority => $callbacks ){

			foreach( $callbacks as $key => $callback ){

				$function_name = $this->get_callback_name( $key, $callback );

				printf( "%s - %s<br>",esc_html($function_name), esc_html($priority) );
			}  
		}  
	}

	function get_callback_name( $key, $callback ){

		$function = $callback['function'] ?? false;

		if( ! $function ){
			return;
		}

		$callback_name = '';

		if( is_string($function) ){

			$callback_name = $function;

		} else if( is_array($function) && sizeof($function) ){

			if ( is_object( $function[0] ) ) {

				$callback_name = get_class($function[0] );

			}

			$callback_name .= '&ndash;&gt;' . $function[1];
		
		} else {
			$callback_name = 'An anonymous function with key:' . $key;
		}

		return $callback_name;
	}
}

$show_hooks = new Sola_Show_Hook_Position();
$show_hooks->included_hooks =  array(
    'woocommerce_before_main_content',
    'woocommerce_after_main_content',
    'woocommerce_sidebar',
    'woocommerce_before_single_product',
    'woocommerce_before_single_product_summary',
    'woocommerce_single_product_summary',
    'woocommerce_after_single_product_summary',
    'woocommerce_after_single_product',
);
$show_hooks->run();

如何禁用product gallery的功能

下面的代码演示如何禁用product gallery的灯箱、幻灯片以及鼠标悬停时放大图片的功能,代码放在主题的functions.php中。

add_action( 'after_setup_theme', 'sola_remove_woocommerce_features',100);
function sola_remove_woocommerce_features(){
	remove_theme_support( 'wc-product-gallery-lightbox' );
	remove_theme_support( 'wc-product-gallery-slider' );
	remove_theme_support( 'wc-product-gallery-zoom' );
}

禁用后,点击gallery里的主图片,会在新窗口打开图片。

如果开启了elementor插件,则点击图片依然会用lightbox打开,这是因为elementor默认使所有图片都有灯箱效果,要禁用,需要到elementor的全局设置里关闭。