Add WooCommerce-to-Twig rendering bridge

Intercept WooCommerce's PHP template loading via
woocommerce_before_template_part / woocommerce_after_template_part hooks
to render Bootstrap 5 Twig templates instead. This makes all 99 child
theme templates functional in a standard WooCommerce environment.

- Create WooCommerceExtension (Twig AbstractExtension) with ~50 functions
  and 7 filters covering WC API, WordPress hooks, escaping, and forms
- Rewrite TemplateOverride to use hook-based interception with stack-based
  output buffering for nested template support
- Wire bridge initialization at init priority 20 in functions.php
- Fix invalid {% do return() %} in two order templates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 11:15:59 +01:00
parent 49b1d52701
commit 46eb7f0a22
5 changed files with 538 additions and 136 deletions

View File

@@ -18,46 +18,44 @@
# @since 0.1.0
#}
{% if not apply_filters('woocommerce_order_item_visible', true, item) %}
{% do return() %}
{% endif %}
{% if apply_filters('woocommerce_order_item_visible', true, item) %}
{% set is_visible = product and product.is_visible() %}
{% set product_permalink = apply_filters('woocommerce_order_item_permalink', is_visible ? product.get_permalink(item) : '', item, order) %}
{% set is_visible = product and product.is_visible() %}
{% set product_permalink = apply_filters('woocommerce_order_item_permalink', is_visible ? product.get_permalink(item) : '', item, order) %}
<tr class="{{ apply_filters('woocommerce_order_item_class', 'woocommerce-table__line-item order_item', item, order) }}">
<td class="product-name">
{% if product_permalink %}
<a href="{{ product_permalink|esc_url }}">{{ item.get_name()|esc_html }}</a>
{% else %}
{{ item.get_name()|esc_html }}
{% endif %}
{% set qty = item.get_quantity() %}
{% set refunded_qty = order.get_qty_refunded_for_item(item_id) %}
<strong class="product-quantity">
{% if refunded_qty %}
&times;&nbsp;<del>{{ qty }}</del> <ins>{{ qty - (refunded_qty * -1) }}</ins>
<tr class="{{ apply_filters('woocommerce_order_item_class', 'woocommerce-table__line-item order_item', item, order) }}">
<td class="product-name">
{% if product_permalink %}
<a href="{{ product_permalink|esc_url }}">{{ item.get_name()|esc_html }}</a>
{% else %}
&times;&nbsp;{{ qty }}
{{ item.get_name()|esc_html }}
{% endif %}
</strong>
{{ do_action('woocommerce_order_item_meta_start', item_id, item, order, false) }}
{{ wc_display_item_meta(item) }}
{{ do_action('woocommerce_order_item_meta_end', item_id, item, order, false) }}
</td>
{% set qty = item.get_quantity() %}
{% set refunded_qty = order.get_qty_refunded_for_item(item_id) %}
<td class="product-total text-end">
{{ order.get_formatted_line_subtotal(item)|raw }}
</td>
</tr>
<strong class="product-quantity">
{% if refunded_qty %}
&times;&nbsp;<del>{{ qty }}</del> <ins>{{ qty - (refunded_qty * -1) }}</ins>
{% else %}
&times;&nbsp;{{ qty }}
{% endif %}
</strong>
{% if show_purchase_note and purchase_note %}
<tr class="product-purchase-note">
<td colspan="2">
{{ purchase_note|wp_kses_post|wpautop }}
{{ do_action('woocommerce_order_item_meta_start', item_id, item, order, false) }}
{{ wc_display_item_meta(item) }}
{{ do_action('woocommerce_order_item_meta_end', item_id, item, order, false) }}
</td>
<td class="product-total text-end">
{{ order.get_formatted_line_subtotal(item)|raw }}
</td>
</tr>
{% if show_purchase_note and purchase_note %}
<tr class="product-purchase-note">
<td colspan="2">
{{ purchase_note|wp_kses_post|wpautop }}
</td>
</tr>
{% endif %}
{% endif %}