Thanks for all the code, but I am stuck with a couple of questions.
I am currently developing a theme with the Sage 9.0.0-beta.4 library and Woocommerce integration.
What version of Sage did you use for the code you posted? I more or less get a result when the original files in the Woocommerce plugin directory (archive-product.php & single-product.php) are completely quoted out and I add echo App\Template('woocommerce'); at the end of these files.
Then everything loads fine, but when you would update Woocommerce it will override these changes. This is, offcourse, a unwanted situation. It needs to keep working no matter what I would update.
Did you do any of these things, or just left them as is when you install Woocommerce for the first time?
Also, which Controller are you using? Is it under resources/controllers or app/Controllers ?
I tried both but didn’t get any result.
To get this straight, these are the steps you took:
-
Created a file named woocommerce.blade.php in resources/views containing the @php(App\woocommerce_content(get_defined_vars()))
-
You are showing the original wc_get_template_part function from wc-core-functions.php in the Woocommerce plugin directory. But didn’t do anything with it right?
-
The code you applied to override the original wc_get_template_part function and did you put it in filters.php under themes/your_theme_name/app or setup.php, or just changed the original?
In case of php files you use the first piece of code and incase of blade files you use the second? Or you just put both in them and let the site figure it out?
-
Here you update the woocommerce_content() function with your own, did you let the original(Woocommerce plugin directory) be as is and put this code in the setup.php under themes/your_theme_name/app or just edited the original?
-
You apply some code to the archive.blade.php & single-product.blade.php pretty straight forward.
Thanks in advance!
Edit December 22nd
Together with a colleague found out that we had to change the following to get the correct templates:
1. Edit helpers.php
Edit the function filter_templates in helpers.php under themes/your_theme_name/app to look in the resources/views/woocommerce directory like so:
function filter_templates($templates)
{
return collect($templates)
->map(function ($template) {
return preg_replace('#\.(blade\.)?php$#', '', ltrim($template));
})
->flatMap(function ($template) {
$paths = apply_filters('sage/filter_templates/paths', ['views', 'resources/views', 'resources/views/woocommerce']);
return collect($paths)
->flatMap(function ($path) use ($template) {
return [
"{$path}/{$template}.blade.php",
"{$path}/{$template}.php",
"{$template}.blade.php",
"{$template}.php",
];
});
})
->filter()
->unique()
->all();
}
This lets the function locate_template also look in the resources/views/woocommerce folder, where we declare our blade templates
2. Change the wc_get_template_part because of #1.
Wherever locate_template is used look for resources/views . WC()->template_path() . and remove the resources/views part of it.
This change needs to be done because the filter_templates function, which we changed in #1 already looks in the resources/views directory but not the resources/views/woocommerce directory.
- In the
resources root we created a directory named woocommerce with the archive-product.php (for example) which contained this:
<?php
$template = 'woocommerce.archive-product';
$data = collect(get_body_class())->reduce(function ($data, $class) use ($template) {
return apply_filters("sage/template/{$class}/data", $data, $template);
}, []);
echo App\Template($template, $data);
Notice that $template is filled with the woocommerce.archive-product, this is done so the template that we look for in resources/views/woocommerce is archive-product.blade.php
4. create archive-product.blade.php and more
As you could have guessed reading #3 we need to create a archive-product.blade.php in the resources/views/woocommerce folder.
This file contains the normal blade template stuff like @extends and all that yada yada. But also this:
@while(have_posts()) @php(the_post())
{{ App\wc_get_template_part('content', 'product', null, get_defined_vars()) }}
@endwhile
With this we load a new blade template for the products named content-product.blade.php (more on #5) and we really get control over our Woocommerce content, you can style it the way you would like, fetch the needed data etc.
5. Create the last blade view and see the awesomeness rise
As mentioned in #4 we also created a new content-product.blade.php because Woocommerce loads this file by default, and we wanted control over this. So we looked it up in the Woocommerce plugin templates folder and found it there, so we created our own file named content-product.blade.php in the resources/views/woocommerce folder which contains:
@php
global $product;
@endphp
<li @php(post_class())>
{!! do_action( 'woocommerce_before_shop_loop_item' ) !!}
{!! do_action( 'woocommerce_before_shop_loop_item_title' ) !!}
{!! do_action( 'woocommerce_shop_loop_item_title' ) !!}
{!! do_action( 'woocommerce_after_shop_loop_item_title' ) !!}
{!! do_action( 'woocommerce_after_shop_loop_item' ) !!}
</li>
The do_action's are just tests. With a simple var_dump you can check the contents of $product and use as needed.
The same goes for all the other files that Woocommerce loads when viewing a product, ordering etc.
Hope this helps someone who crashed at the same point I did.
Edit 8th of January
I further changed the setup.php so I can call wc_get_template function within blade files.
Here is what I added to the setup.php:
function wc_locate_template( $template_name, $template_path = '', $default_path = '' ) {
if ( ! $template_path ) {
$template_path = WC()->template_path();
}
// Look within passed path within the theme - this is priority.
$template = locate_template(
array(
trailingslashit( $template_path ) . $template_name,
$template_name,
)
);
// Get default template/
if ( ! $template || WC_TEMPLATE_DEBUG_MODE ) {
$template = $default_path . $template_name;
}
// Return what we found.
return apply_filters( 'woocommerce_locate_template', $template, $template_name, $template_path );
}
function wc_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
if ( ! empty( $args ) && is_array( $args ) ) {
extract( $args );
}
$located = wc_locate_template( $template_name, $template_path, $default_path );
if ( ! file_exists( $located ) ) {
wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '<code>' . $located . '</code>' ), '2.1' );
return;
}
// Allow 3rd party plugin filter template file from their plugin.
$located = apply_filters( 'wc_get_template', $located, $template_name, $args, $template_path, $default_path );
do_action( 'woocommerce_before_template_part', $template_name, $template_path, $located, $args );
include( $located );
do_action( 'woocommerce_after_template_part', $template_name, $template_path, $located, $args );
}
The filters.php file already had this, like mentioned in @mtxz 's example:
add_filter('wc_get_template', function ($located, $template_name, $args, $template_path, $default_path) {
$bladeTemplateName = str_replace('.php', '.blade.php', $template_name);
$bladeTemplate = locate_template([$bladeTemplateName, 'resources/views/' . WC()->template_path() . $bladeTemplateName]);
if ($bladeTemplate) {
return template_path($bladeTemplate, $args);
}
return $located;
}, PHP_INT_MAX, 5);
In the wc_locate_template function I made a minor change, basically just deleted one if statement:
if ( ! $default_path ) {
$default_path = WC()->plugin_path() . '/templates/';
}
I copied it’s contents from woocommerce/includes/wp-core-functions.php, therefore this delete. It looks for the templates directory in the plugin folder.
With this code added to the setup.php I can make use of custom templates instead of parts.
Example:
Woocommerce makes use of the wc_get_template function to call the title of a single product.
I wanted some extra flexibility with this, so I can customize the title.php file without it being overwritten whenever Woocommerce updated.
Now I have this folder structure under views (sage 9):
views
| - woocommerce (here live all the basic Woocommerce related blade files)
| | - single-product (here live my custom templates related to single product)
| | | title.php etc.
| | archive.product.blade.php etc.
| 404.blade.php etc.
Like the folder single-product I am now able to add directories for each individual directory(that I want to override) that exists in Woocommerce’s templates directory and write my own code based on the original files.
So I created the directory single-product under the woocommerce directory in the views directory.
The templates are being used in the following order:
single-product.blade.php
-
single-product.blade.php looks for content-single-product.blade.php
-
content-single-product.blade.php looks for single-product/title.php
If found confusing, look in the templates directory in the Woocommerce plugin, it will make more sense.
single-product.blade.php
@extends('layouts.app')
@include('partials.headers.woocommerce-header')
@section('content')
<div class="container">
<div class="row">
@while(have_posts()) @php(the_post())
{{ App\wc_get_template_part('content', 'single-product', null, get_defined_vars()) }}
@endwhile
</div>
</div>
@endsection
Here we call for the wc_get_template_part function located under the App namespace.
It will look for a template named content-single-product within the same directory.
content-single-product.blade.php
@php
global $product;
@endphp
<div class="col-6">
<div class="img-fluid">
{{ App\wc_get_template('single-product/image.php') }}
</div>
</div>
<div class="col-6">
{{ App\wc_get_template('single-product/title.php') }}
</div>
In this file, we go hunting for the image.php and title.php in the single-product directory, also under the App namespace.
single-product/title.php
the_title( '<h1 class="product_title entry-title">', '</h1>' );
Here we will load the title with it’s size & classes.
Hope this makes sense & helps