woocommerce 商品ページのレイアウトを変更する。

作業の背景

テーマはflatsomeを使用してます。

div.product-footer内に表示されているコンテンツをdiv.product-infoに表示させたいとのこと。

該当のテンプレートファイルは、\theme\flatsome\woocomeerce\single-procut\layout\prduct.php

ソースを確認すると、

do_action( ‘woocommerce_single_product_summary’ );で、div.product-info内にコンテンツを表示

do_action( ‘woocommerce_after_single_product_summary’ );で、 div.product-footer内 でコンテンツを表示している。

移動させたいのは、「追加情報(additional informationの表)」。

「追加情報(additional informationの表)」 は、woocommerce_output_product_data_tabsをフックさせて表示させている。

なので、

remove_action('woocommerce_after_single_product_summary','woocommerce_output_product_data_tabs');
add_action('woocommerce_single_product_summary','woocommerce_output_product_data_tabs');

のようにfunctions.phpに書いて、コンテンツの移動を行った。

しかし、この方法だと、 woocommerce_output_product_data_tabs で表示するタブがすべて移動しています。今回は、タブは1つしか表示していないので問題はないといえばないのだが、将来的に複数のタブを表示するようにした場合に意図に沿わない結果になるだろう。

なので、 woocommerce_output_product_data_tabs で表示している「product_additional_information_tab」のみを移動するように修正した。

解析

まずは、 「product_additional_information_tab」 がどのように表示するようになっているのか解読する必要がある。

product.php のdo_action( ‘woocommerce_after_single_product_summary’ ); から、ソースコードを検索して遡っていく。

woocommerce_after_single_product_summary にフックしているwoocommerce_output_product_data_tabsで検索。

\wp-content\plugins\woocommerce\includes\wc-template-functions.phpの1598表目にfucntion woocommerce_output_product_data_tabs が定義されている。

if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) {

	/**
	 * Output the product tabs.
	 */
	function woocommerce_output_product_data_tabs() {
		wc_get_template( 'single-product/tabs/tabs.php' );
	}
}

テンプレートファイルの single-product/tabs/tabs.php を呼び出しているので、tabs.phpを開いてみる

/**
 * Filter tabs and allow third parties to add their own.
 *
 * Each tab is an array containing title, callback and priority.
 *
 * @see woocommerce_default_product_tabs()
 */
$product_tabs = apply_filters( 'woocommerce_product_tabs', array() );

$tab_count   = 0;
$panel_count = 0;

if ( ! empty( $product_tabs ) ) : ?>

	<div class="woocommerce-tabs wc-tabs-wrapper container tabbed-content">
		<ul class="tabs wc-tabs product-tabs small-nav-collapse <?php flatsome_product_tabs_classes(); ?>" role="tablist">
			<?php foreach ( $product_tabs as $key => $product_tab ) : ?>
				<li class="<?php echo esc_attr( $key ); ?>_tab <?php if ( $tab_count == 0 ) echo 'active'; ?>" id="tab-title-<?php echo esc_attr( $key ); ?>" role="tab" aria-controls="tab-<?php echo esc_attr( $key ); ?>">
					<a href="#tab-<?php echo esc_attr( $key ); ?>">
						<?php echo wp_kses_post( apply_filters( 'woocommerce_product_' . $key . '_tab_title', $product_tab['title'], $key ) ); ?>
					</a>
				</li>
				<?php $tab_count++; ?>
			<?php endforeach; ?>
		</ul>
		<div class="tab-panels">
			<?php foreach ( $product_tabs as $key => $product_tab ) : ?>
				<div class="woocommerce-Tabs-panel woocommerce-Tabs-panel--<?php echo esc_attr( $key ); ?> panel entry-content <?php if ( $panel_count == 0 ) echo 'active'; ?>" id="tab-<?php echo esc_attr( $key ); ?>" role="tabpanel" aria-labelledby="tab-title-<?php echo esc_attr( $key ); ?>">
					<?php if ( $key == 'description' && ux_builder_is_active() ) echo flatsome_dummy_text(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
					<?php
					if ( isset( $product_tab['callback'] ) ) {
						call_user_func( $product_tab['callback'], $key, $product_tab );
					}
					?>
				</div>
				<?php $panel_count++; ?>
			<?php endforeach; ?>

			<?php do_action( 'woocommerce_product_after_tabs' ); ?>
		</div>
	</div>

<?php endif; ?>

表示するタブのデータの配列を $product_tabs = apply_filters( ‘woocommerce_product_tabs’, array() ); で受け取っているから、このフィルターを使って、配列から削除すればよいだろう。

表を表示する部分は、call_user_funcで呼び出している。

$product_tabs をvar_dumpして確認すると

array(1) {
  ["additional_information"]=>
  array(3) {
    ["title"]=>
    string(12) "追加情報"
    ["priority"]=>
    int(20)
    ["callback"]=>
    string(46) "woocommerce_product_additional_information_tab"
  }
}

となっている。

なので、 woocommerce_product_additional_information_tab を、
do_action( ‘woocommerce_single_product_summary’ );に、フックしてdiv.product-info内にコンテンツを表示すればよいということになる。

ちなみに、function woocommerce_product_additional_information_tabはどこで定義されているか検索してみると、
\wp-content\plugins\woocommerce\includes\wc-template-functions.phpの1824行目で次のとおり。

if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {

	/**
	 * Output the attributes tab content.
	 */
	function woocommerce_product_additional_information_tab() {
		wc_get_template( 'single-product/tabs/additional-information.php' );
	}
}

てな具合で、テンプレートファイルのsingle-product/tabs/additional-information.phpを呼び出しているだけの関数。

結論のコード

// prodct.php>#product-footer に表示される「追加情報」のタブを非表示にする
add_filter( 'woocommerce_product_tabs', function($product_tabs){
				unset($product_tabs['additional_information']);
				return($product_tabs);
			}
);
//非表示にしたタブをprodct.php>#product-infoに表示する。
add_action('woocommerce_single_product_summary','woocommerce_product_additional_information_tab');

てな具合で完成。

追記

「追加情報」(属性の一覧表)周りのカスタマイズも頼まれたので、そのぶぶんも備忘録として追記。

テンプレートファイルのsingle-product/tabs/additional-information.php の中身は

defined( 'ABSPATH' ) || exit;

global $product;

$heading = apply_filters( 'woocommerce_product_additional_information_heading', __( 'Additional information', 'woocommerce' ) );

?>

<?php if ( $heading ) : ?>
	<h2><?php echo esc_html( $heading ); ?></h2>
<?php endif; ?>

<?php do_action( 'woocommerce_product_additional_information', $product ); ?>

なので、woocommerce_product_additional_information にフックしている関数を探すと、
wc-template-hooks.phpの201行目の

add_action( 'woocommerce_product_additional_information', 'wc_display_product_attributes', 10 );

だけ。その中身は

/**
 * Outputs a list of product attributes for a product.
 *
 * @since  3.0.0
 * @param  WC_Product $product Product Object.
 */
function wc_display_product_attributes( $product ) {
	$product_attributes = array();

	// Display weight and dimensions before attribute list.
	$display_dimensions = apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() );

	if ( $display_dimensions && $product->has_weight() ) {
		$product_attributes['weight'] = array(
			'label' => __( 'Weight', 'woocommerce' ),
			'value' => wc_format_weight( $product->get_weight() ),
		);
	}

	if ( $display_dimensions && $product->has_dimensions() ) {
		$product_attributes['dimensions'] = array(
			'label' => __( 'Dimensions', 'woocommerce' ),
			'value' => wc_format_dimensions( $product->get_dimensions( false ) ),
		);
	}

	// Add product attributes to list.
	$attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' );
	
	

	foreach ( $attributes as $attribute ) {
		$values = array();

		if ( $attribute->is_taxonomy() ) {
			$attribute_taxonomy = $attribute->get_taxonomy_object();
			$attribute_values   = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) );

			foreach ( $attribute_values as $attribute_value ) {
				$value_name = esc_html( $attribute_value->name );

				if ( $attribute_taxonomy->attribute_public ) {
					$values[] = '<a href="' . esc_url( get_term_link( $attribute_value->term_id, $attribute->get_name() ) ) . '" rel="tag">' . $value_name . '</a>';
				} else {
					$values[] = $value_name;
				}
			}
		} else {
			$values = $attribute->get_options();

			foreach ( $values as &$value ) {
				$value = make_clickable( esc_html( $value ) );
			}
		}

		$product_attributes[ 'attribute_' . sanitize_title_with_dashes( $attribute->get_name() ) ] = array(
			'label' => wc_attribute_label( $attribute->get_name() ),
			'value' => apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ),
		);
	}

	/**
	 * Hook: woocommerce_display_product_attributes.
	 *
	 * @since 3.6.0.
	 * @param array $product_attributes Array of atributes to display; label, value.
	 * @param WC_Product $product Showing attributes for this product.
	 */
	$product_attributes = apply_filters( 'woocommerce_display_product_attributes', $product_attributes, $product );
	
	
	
	wc_get_template(
		'single-product/product-attributes.php',
		array(
			'product_attributes' => $product_attributes,
			// Legacy params.
			'product'            => $product,
			'attributes'         => $attributes,
			'display_dimensions' => $display_dimensions,
		)
	);
}

表の前後に、何かを追加するだけなら、additional-information.phpを子テーマに作って、カスタマイズしてもいいし、do_action( ‘woocommerce_product_additional_information’, $product );にフックさせて、追加してもいい。