# Controller and WooCommerce

**URL:** https://discourse.roots.io/t/controller-and-woocommerce/9469
**Category:** sage
**Created:** 2017-04-26T22:53:48Z
**Posts:** 19

## Post 1 by @vdrnn — 2017-04-26T22:53:48Z

I stumbled upon the issue that I can’t access any data from the controller in the woocommerce templates.  
I tried with a custom controller as well as with soberwp/controller and it doesn’t work with both.

I implemented the woocommerce templates according to the solution in this post [Latest Sage 9, Blade, and WooCommerce](https://discourse.roots.io/t/latest-sage-9-blade-and-woocommerce/9462)

Anyone else stumbled upon that problem?

---

## Post 2 by @kalenjohnson — 2017-04-27T03:18:23Z

Are you using regular PHP templates for the Woocommerce ones, or Blade templates? Any data set up in controllers in that way is only going to be available to Blade.

---

## Post 3 by @vdrnn — 2017-04-27T06:38:38Z

Yes, using Blade templates and I get the issue now, thanks.  
Any idea how to overcome the problem though? I’d love to keep using sage 9 with Blade templates for future WooCommerce projects.

---

## Post 4 by @mtxz — 2017-08-23T00:51:32Z

Anyone found a solution about this ? The @debug(‘hierarchy’) is outputing correct hierarchy and i created one of the listed controlers. Thanks.

---

## Post 5 by @mtxz — 2017-08-23T10:48:41Z

I found a fix.

From what i saw, variable was correctly passing to the `woocommerce.blade.php` template.  
But not in included template (lower in hierarchy, `content-product` for example, or in my case other custom template (i added) included in `woocommerce_content()`) because of `woocommerce_content()` method called in `Woocommerce.blade.php`.

Here’s what i’ve done to make it work:

1. Update `Woocommerce.blade.php` (wich should be located in `/resources/views`, **not** within `/resources/views/woocommerce/`) with `@php(App\woocommerce_content(get_defined_vars()))`  
to get all binded variables sent to `woocommerce_content()` - full content:

2. We need override `wc_get_template_part()` method to pass variables, and then pass them to `wc_get_template_part` filter, here is the method (in my theme so) :

3. Update the sage `wc_get_template_part` filter (that i edited a bit to get blade or PHP if not blade in my resources/views/woocommerce) to pass variables to `echo template($bladeTemplate, $args);` see :

> add\_filter(‘wc\_get\_template\_part’, function ($template, $slug, $name, $args) {
> 
> ```
> $bladeTemplate = false;
> 
> // Look in yourtheme/slug-name.blade.php and yourtheme/woocommerce/slug-name.blade.php
> if ($name && !WC_TEMPLATE_DEBUG_MODE) {
> $bladeTemplate = locate_template(["{$slug}-{$name}.blade.php", 'resources/views/' . WC()->template_path() . "{$slug}-{$name}.blade.php"]);
> }
> 
> // If template file doesn't exist, look in yourtheme/slug.blade.php and yourtheme/woocommerce/slug.blade.php
> if (!$template && !WC_TEMPLATE_DEBUG_MODE) {
> $bladeTemplate = locate_template(["{$slug}.blade.php", 'resources/views/' . WC()->template_path() . "{$slug}.blade.php"]);
> }
> 
> if ($bladeTemplate) {
> echo template($bladeTemplate, $args);
> 
> // Return a blank file to make WooCommerce happy
> //return get_theme_file_path('index.php');
> return null;
> }
> 
> //try to look for PHP files within resources/views/woocommerce
> $normalTemplate = false;
> if ($name && !WC_TEMPLATE_DEBUG_MODE) {
> $normalTemplate = locate_template(["{$slug}-{$name}.php", 'resources/views/' . WC()->template_path() . "{$slug}-{$name}.php"]);
> }
> if (!$normalTemplate && !WC_TEMPLATE_DEBUG_MODE) {
> $normalTemplate = locate_template(["{$slug}.php", 'resources/views/' . WC()->template_path() . "{$slug}.php"]);
> }
> 
> if ($normalTemplate) {
> return get_theme_file_path($normalTemplate); //work even without
> }
> 
> return $template;
> ```
> 
> `}, PHP_INT_MAX, 4);`

Here the other Sage filter in case of :

```
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);
```

1. update `woocommerce_content()` method

Now we need to edit the `woocommerce_content()` method called from `Woocommerce.blade.php`, so i added it in my theme to override it:

```
function woocommerce_content($args = [])
{
    if (is_singular('product')) {

        while (have_posts()) : the_post();

            wc_get_template_part('content', 'single-product', null, $args);

        endwhile;
    } else { ?>
        //here i call my custom template with wc_get_template_part and passing args
        <?php if (apply_filters('woocommerce_show_page_title', true)) : ?>
            <h1 class="page-title"><?php /*woocommerce_page_title(); */?></h1>

        <?php endif; ?>

        <?php do_action('woocommerce_archive_description'); ?>

        <?php if (have_posts()) : ?>

            <?php do_action('woocommerce_before_shop_loop'); ?>

            <?php woocommerce_product_loop_start(); ?>

            <?php woocommerce_product_subcategories(); ?>

            <?php while (have_posts()) : the_post(); ?>

                <?php wc_get_template_part('content', 'product', null, $args); ?>

            <?php endwhile; // end of the loop. ?>

            <?php woocommerce_product_loop_end(); ?>

            <?php do_action('woocommerce_after_shop_loop'); ?>

        <?php elseif (!woocommerce_product_subcategories(['before' => woocommerce_product_loop_start(false), 'after' => woocommerce_product_loop_end(false)])) : ?>

            <?php do_action('woocommerce_no_products_found'); ?>

        <?php endif;
    }
}
```

(i can edit this method to add template part depending stuff, eg: a specific part for taxonomy templates).

1. My `archive-product` and `single-product .blade.php` contain (from [Controller not passing data to WooCommerce template · Issue #16 · soberwp/controller · GitHub](https://github.com/soberwp/controller/issues/16)) it’s needed to pass variables to the `woocommerce.blade.php` template):

This is it, now i have access to all my variables from Taxonomy Controller into my Taxonomy template !

In the end :

1. We overrided `woocommerce_content()` called in `woocommerce.blade.php` to pass `get_defined_vars()` (found it used in `Sober WP Controller` here [https://github.com/soberwp/controller/blob/master/controller.php](https://github.com/soberwp/controller/blob/master/controller.php)) as all controller variables are defaultly successfuly binded to `Woocommerce.blade.php`(`archive-product` and `single-product` pass the variables to `woocommerce.blade.php`)
2. `woocommerce_content()` will call `wc_get_template_part()` and i think that is where we loose variables (or directly cause of woocommerce\_content() call). so we edited this method to accept a “`$args`” parameter.
3. This `$args` parameter is passed to the filter `wc_get_template_part` used by Sage to override for blade.
4. In the` sage wc_get_template_part` filter, we pass the `$args` variable to `echo template` to have all the binded variables available (wich render the blade)
5. archive-product and single-product pass the variables to woocommerce.blade.php
6. variables are available in templates
7. I think the default `woocommerce_content()` make system loose variables, as all is defaultly working in `woocommerce.blade.php` but not under.

I don’t know if it’s the good way, but it works as for now.

**EDIT** :  
Idk if related, but title() method from App controller dont work as it’s declared as static method. Removing the static fixes the issue. **EDIT** : as seen in doc, static methods are not called on Controller instanciation to bind returned data to the view, but need to be called from \App. This is really nice in the end to have both :slight_smile:

**EDIT** :  
The override for single-product.php don’t seems to work… Even if the archive-product.php work perfectly, i’m unable to override the single-product.php. If directly update the woocommerce (/wp-plugins/) files, it works. So this is the template override that is not working here, i don’t get why. If anyone have idea. I finally had to tweak the `template_include` filter to change path for `single-product` and correctly get my Blade.

//fix for single-product.php  
if(str\_contains($template, ‘single-product.php’)) {  
$template = get\_stylesheet\_directory() . ‘/views/woocommerce/single-product.blade.php’;  
}

---

## Post 6 by @marekvrofski — 2017-12-22T08:31:45Z

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:

1. Created a file named `woocommerce.blade.php` in `resources/views` containing the `@php(App\woocommerce_content(get_defined_vars()))`

2. 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?

3. 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?

1. 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?

2. 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.

1. 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:

1. `single-product.blade.php`
2. `single-product.blade.php` looks for `content-single-product.blade.php`
3. `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

---

## Post 7 by @Twansparant — 2018-01-10T13:39:45Z

Wow thanks for all the effort!

If I understand correctly, the difference with @arthurkirkosa approach in [this topic](https://discourse.roots.io/t/any-working-example-of-sage-9-latest-sage-9-0-0-beta-4-with-woocommerce-3-1-1/10099) is that the **woocommerce** folder is located directly under **resources** and you won’t be able to use blade templates right?

Still pretty confusing though with all the different snippets from you and @mtxz  
Any chance I could have a look at your setup code in a gist or something like that?

That would be awesome!  
Thanks

---

## Post 8 by @marekvrofski — 2018-01-10T13:54:55Z

No problem!

After reading it is a little confusing indeed, I will try to setup a Gist/Github directory with all the needed code within the next week.

I didn’t use any of the code from the topic you mention, when the Gist/Github directory is up and running I will post a link!

### 2 hours later

[Here](https://github.com/MarekVrofski/Sage-Woocommerce) is the Github Repository with a readme, feel free to post issues, fork it etc.

Hope this helps!

---

## Post 9 by @Twansparant — 2018-01-10T14:03:27Z

That would be brilliant!  
I’m setting up a WooCommerce site too at the moment, and would really like to use the whole Sage setup with Blade templating and the Sober controllers like i do with ‘normal’ projects :slight_smile:

---

## Post 10 by @johnmn — 2018-01-10T15:18:08Z

> [@marekvrofski](#):
>
> After reading it is a little confusing indeed, I will try to setup a Gist/Github directory with all the needed code within the next week.
> 
> I didn’t use any of the code from the topic you mention, when the Gist/Github directory is up and running I will post a [link](https://proweb365.com)!

.  
. Please update the result when you get done. I also really confuse about the code…

---

## Post 11 by @Twansparant — 2018-01-11T14:00:19Z

I’m trying to implement your code, but I’m missing your **woocommerce-header**.  
Is this essential for your setup?

```
@include('partials.headers.woocommerce-header')
```

Thanks!

**Update:**  
Never mind, I don’t think the `@include('partials.headers.woocommerce-header')` rule is neccessary.

Everything is working pretty well actually!

I removed the `woocommerce_content` & `wc_get_template` functions from your **app/setup.php** and the custom **archive-product** & **single-product** templates are still working!

I also think you’re working with an older version of WooCommerce, because I’m getting errors from this file: **resources/views/woocommerce/content-single-product.blade.php**

Looking at the original WooCommerce templates from the plugin folder, these 2 templates don’t exist in WC version **3.2.6** :

```
{{ App\wc_get_template('add-to-cart/simple.php') }}
{{ App\wc_get_template('single-product/reviews.php') }}
{{ App\wc_get_template_part('single', 'product-review') }}
```

If I replace them with:

```
{{ App\wc_get_template('single-product/add-to-cart/simple.php') }}
{{ App\wc_get_template('single-product-reviews.php') }}
```

The errors are gone!

Thanks!

---

## Post 12 by @marekvrofski — 2018-01-11T14:45:07Z

I noticed someone else on Github had the same [issue](https://github.com/MarekVrofski/Sage-Woocommerce/issues/1), I will try to update the repo tonight.

@Twansparant Great to hear that the issue has been resolved.

I indeed have a ‘older’ version, 3.2.5 :sweat_smile:

Tonight I will have a look if the `woocommerce_content` & `wc_get_template` functions can be removed from the `app/setup.php`, this could be because they also exist in `app/filters.php`.

The last part of your post is preference related. In the Woocommerce version I have installed, I found it very neat that the `add-to-cart` etc. are in their own folder within the Woocommerce folder, therefore I implemented it the same way.

Also because then you can use just “one” file for all the products to be “added to the cart” (I hope) if you get what I mean.

---

## Post 13 by @Twansparant — 2018-01-11T15:05:36Z

> [@marekvrofski](#):
>
> The last part of your post is preference related. In the Woocommerce version I have installed, I found it very neat that the add-to-cart etc. are in their own folder within the Woocommerce folder, therefore I implemented it the same way.

Ah ok, but that file you referenced was not in the repo, that’s why it was causing errors.

---

## Post 14 by @marekvrofski — 2018-01-11T15:08:15Z

Right, I thought it would be unnecessary to include all the template files I use in my code, since we are all developers :wink:

If wanted/needed I could still provide them :+1:

---

## Post 15 by @Twansparant — 2018-01-11T15:17:25Z

No that’s allright, I was just checking if the path was correct :slight_smile:

---

## Post 16 by @JanneR — 2018-01-15T16:59:11Z

Did anyone test the variable product and get it to work correctly with these instructions and code snippets? For example, the product image or price will be updated to the product view when the variation changes. In my testing, the price of a variation product will only change in the shopping cart and not in a single product view as it should work.

---

## Post 17 by @Twansparant — 2018-01-16T15:24:39Z

> Did anyone test the variable product and get it to work correctly with these instructions and code snippets?

I just did, and can confirm the price variations are working fine for me in the **archive-product** and **single-product** view?

---

## Post 18 by @mtxz — 2018-06-08T16:26:44Z

Please refer to :

- [https://github.com/mtx-z/Sage9-Woocommerce-Integration](https://github.com/mtx-z/Sage9-Woocommerce-Integration)
- [https://github.com/MarekVrofski/Sage-Woocommerce](https://github.com/MarekVrofski/Sage-Woocommerce)

---

## Post 19 by @Gerasimenko_Maksim — 2021-01-12T20:21:43Z

Hello i found issue

you need in filters.php

replace add\_filter(‘template\_include’, function ($template) {  
…

}

to

add\_filter(‘template\_include’, function ($template) {  
$isTax = str\_contains($template,‘taxonomy-product-cat’);

```
if (!$isTax) {
    collect( ['get_header', 'wp_head'] )->each( function ( $tag ) {
        ob_start();
        do_action( $tag );
        $output = ob_get_clean();
        remove_all_actions( $tag );
        add_action( $tag, function () use ( $output ) {
            echo $output;
        } );
    } );
    $data = collect( get_body_class() )->reduce( function ( $data, $class ) use ( $template ) {
        return apply_filters( "sage/template/{$class}/data", $data, $template );
    }, [] );
    
    if ( $template ) {
        echo template( $template, $data );
        return get_stylesheet_directory() . '/index.php';
    }
    
    return $template;
} else {
    return $template;
}
```

}, PHP\_INT\_MAX);
