├── .editorconfig ├── .gitignore ├── README.md ├── composer.json ├── publishes ├── app │ └── wc-template-hooks.php └── resources │ └── views │ ├── archive-product.blade.php │ └── single-product.blade.php └── src ├── WooCommerce.php └── WooCommerceServiceProvider.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.php] 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | .idea/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sage WooCommerce 2 | 3 | Add WooCommerce support to Sage 10. 4 | 5 | ## Installation 6 | 7 | Install the composer package. 8 | 9 | composer require generoi/sage-woocommerce 10 | 11 | Add the package to the cached package manifest. 12 | 13 | wp acorn package:discover 14 | 15 | Publish the required `single-product.blade.php` and `archive-product.blade.php` views. 16 | 17 | wp acorn vendor:publish --tag="woocommerce-template-views" 18 | 19 | Optionally publish a commented out `app/wc-template-hooks.php` file for customizing the WC template hooks. 20 | 21 | wp acorn vendor:publish --tag="woocommerce-template-hooks" 22 | 23 | By default your theme has now declared WooCommerce support. To add support for specific features, add them to your `app/setup.php` 24 | 25 | ```php 26 | add_theme_support('wc-product-gallery-zoom'); 27 | add_theme_support('wc-product-gallery-lightbox'); 28 | add_theme_support('wc-product-gallery-slider'); 29 | ``` 30 | 31 | ## Filters 32 | 33 | ```php 34 | /** 35 | * Add support for WooCommerce Subscription templates. 36 | */ 37 | add_filter('sage-woocommerce/templates', function ($paths) { 38 | $paths[] = WP_PLUGIN_DIR . '/woocommerce-subscriptions/templates/'; 39 | return $paths; 40 | }); 41 | ``` 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generoi/sage-woocommerce", 3 | "type": "library", 4 | "license": "MIT", 5 | "description": "", 6 | "homepage": "https://github.com/generoi/sage-woocommerce", 7 | "authors": [ 8 | { 9 | "name": "Oskar Schöldström", 10 | "email": "public@oxy.fi" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "Genero\\Sage\\WooCommerce\\": "src/" 16 | } 17 | }, 18 | "require": { 19 | "roots/acorn": "*" 20 | }, 21 | "require-dev": { 22 | "squizlabs/php_codesniffer": "~3.0" 23 | }, 24 | "minimum-stability": "dev", 25 | "prefer-stable": true, 26 | "scripts": { 27 | "test": [ 28 | "phpcs --ignore=vendor --extensions=php --standard=PSR2 ." 29 | ] 30 | }, 31 | "archive" : { 32 | "exclude": [ 33 | ".gitignore" 34 | ] 35 | }, 36 | "extra": { 37 | "acorn": { 38 | "providers": [ 39 | "Genero\\Sage\\WooCommerce\\WooCommerceServiceProvider" 40 | ] 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /publishes/app/wc-template-hooks.php: -------------------------------------------------------------------------------- 1 | 26 | @if (apply_filters('woocommerce_show_page_title', true)) 27 |

{!! woocommerce_page_title(false) !!}

28 | @endif 29 | 30 | @php 31 | do_action('woocommerce_archive_description') 32 | @endphp 33 | 34 | 35 | @if (woocommerce_product_loop()) 36 | @php 37 | do_action('woocommerce_before_shop_loop'); 38 | woocommerce_product_loop_start(); 39 | @endphp 40 | 41 | @if (wc_get_loop_prop('total')) 42 | @while (have_posts()) 43 | @php 44 | the_post(); 45 | do_action('woocommerce_shop_loop'); 46 | wc_get_template_part('content', 'product'); 47 | @endphp 48 | @endwhile 49 | @endif 50 | 51 | @php 52 | woocommerce_product_loop_end(); 53 | do_action('woocommerce_after_shop_loop'); 54 | @endphp 55 | @else 56 | @php 57 | do_action('woocommerce_no_products_found') 58 | @endphp 59 | @endif 60 | 61 | @php 62 | do_action('woocommerce_after_main_content'); 63 | do_action('get_sidebar', 'shop'); 64 | do_action('get_footer', 'shop'); 65 | @endphp 66 | @endsection 67 | -------------------------------------------------------------------------------- /publishes/resources/views/single-product.blade.php: -------------------------------------------------------------------------------- 1 | {{-- 2 | The Template for displaying all single products 3 | 4 | This template can be overridden by copying it to yourtheme/woocommerce/single-product.php. 5 | 6 | HOWEVER, on occasion WooCommerce will need to update template files and you 7 | (the theme developer) will need to copy the new files to your theme to 8 | maintain compatibility. We try to do this as little as possible, but it does 9 | happen. When this occurs the version of the template file will be bumped and 10 | the readme will list any important changes. 11 | 12 | @see https://docs.woocommerce.com/document/template-structure/ 13 | @package WooCommerce\Templates 14 | @version 1.6.4 15 | --}} 16 | 17 | @extends('layouts.app') 18 | 19 | @section('content') 20 | @php 21 | do_action('get_header', 'shop'); 22 | do_action('woocommerce_before_main_content'); 23 | @endphp 24 | 25 | @while(have_posts()) 26 | @php 27 | the_post(); 28 | wc_get_template_part('content', 'single-product'); 29 | @endphp 30 | @endwhile 31 | 32 | @php 33 | do_action('woocommerce_after_main_content'); 34 | do_action('get_sidebar', 'shop'); 35 | do_action('get_footer', 'shop'); 36 | @endphp 37 | @endsection 38 | -------------------------------------------------------------------------------- /src/WooCommerce.php: -------------------------------------------------------------------------------- 1 | app = $app; 24 | $this->fileFinder = $fileFinder; 25 | $this->sageFinder = $sageFinder; 26 | } 27 | 28 | /** 29 | * Load template hook overrides file if available in app/ folder of theme. 30 | */ 31 | public function loadThemeTemplateHooks() 32 | { 33 | locate_template('app/wc-template-hooks.php', true, true); 34 | } 35 | 36 | /** 37 | * Declare theme support. 38 | */ 39 | public function addThemeSupport(): void 40 | { 41 | add_theme_support('woocommerce'); 42 | } 43 | 44 | /** 45 | * Support blade templates for the main template include. 46 | */ 47 | public function templateInclude(string $template): string 48 | { 49 | if (!$this->isWooCommerceTemplate($template)) { 50 | return $template; 51 | } 52 | return $this->locateThemeTemplate($template) ?: $template; 53 | } 54 | 55 | /** 56 | * Support blade templates for the woocommerce comments/reviews. 57 | */ 58 | public function reviewsTemplate(string $template): string 59 | { 60 | if (!$this->isWooCommerceTemplate($template)) { 61 | return $template; 62 | } 63 | 64 | return $this->template($template); 65 | } 66 | 67 | /** 68 | * Filter a template path, taking into account theme templates and creating 69 | * blade loaders as needed. 70 | */ 71 | public function template(string $template, string $templateName = ''): string 72 | { 73 | // Locate any matching template within the theme. 74 | $themeTemplate = $this->locateThemeTemplate($templateName ?: $template); 75 | if (!$themeTemplate) { 76 | return $template; 77 | } 78 | 79 | // Return filename for status screen 80 | if ( 81 | is_admin() && 82 | !wp_doing_ajax() && 83 | get_current_screen() && 84 | get_current_screen()->id === 'woocommerce_page_wc-status' 85 | ) { 86 | return $themeTemplate; 87 | } 88 | 89 | // Include directly unless it's a blade file. 90 | if (!Str::endsWith($themeTemplate, '.blade.php')) { 91 | return $themeTemplate; 92 | } 93 | 94 | // We have a template, create a loader file and return it's path. 95 | return view( 96 | $this->fileFinder->getPossibleViewNameFromPath(realpath($themeTemplate)) 97 | )->makeLoader(); 98 | } 99 | 100 | /** 101 | * Check if template is a WooCommerce template. 102 | */ 103 | protected function isWooCommerceTemplate(string $template): bool 104 | { 105 | return $this->relativeTemplatePath($template) !== $template; 106 | } 107 | 108 | /** 109 | * Return the theme relative template path. 110 | */ 111 | protected function relativeTemplatePath(string $template): string 112 | { 113 | $defaultPaths = [ 114 | // WooCommerce plugin templates 115 | \WC_ABSPATH . 'templates/', 116 | ]; 117 | 118 | if (is_child_theme()) { 119 | // Parent theme templates in woocommerce/ subfolder. 120 | $defaultPaths[] = get_template_directory() . '/' . WC()->template_path(); 121 | } 122 | 123 | return str_replace( 124 | apply_filters('sage-woocommerce/templates', $defaultPaths), 125 | '', 126 | $template 127 | ); 128 | } 129 | 130 | /** 131 | * Locate the theme's WooCommerce blade template when available. 132 | */ 133 | protected function locateThemeTemplate(string $template): string 134 | { 135 | // Absolute plugin template path -> woocommerce/single-product.php 136 | $themeTemplate = WC()->template_path() . $this->relativeTemplatePath($template); 137 | // Return absolute theme template path. 138 | return locate_template($this->sageFinder->locate($themeTemplate)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/WooCommerceServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('woocommerce', WooCommerce::class); 17 | } 18 | 19 | /** 20 | * Bootstrap any application services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | if (defined('WC_ABSPATH')) { 27 | $this->app['woocommerce']->loadThemeTemplateHooks(); 28 | $this->bindSetupAction(); 29 | $this->bindFilters(); 30 | } 31 | 32 | $this->publishes([ 33 | __DIR__ . '/../publishes/resources/views' => $this->app->resourcePath('views/woocommerce'), 34 | ], 'woocommerce-template-views'); 35 | 36 | $this->publishes([ 37 | __DIR__ . '/../publishes/app/wc-template-hooks.php' => $this->app->path('wc-template-hooks.php'), 38 | ], 'woocommerce-template-hooks'); 39 | } 40 | 41 | public function bindFilters() 42 | { 43 | $woocommerce = $this->app['woocommerce']; 44 | 45 | add_filter('template_include', [$woocommerce, 'templateInclude'], 11); 46 | add_filter('woocommerce_locate_template', [$woocommerce, 'template'], 11, 2); 47 | add_filter('woocommerce_locate_core_template', [$woocommerce, 'template'], 10, 2); 48 | add_filter('wc_get_template_part', [$woocommerce, 'template']); 49 | add_filter('wc_get_template', [$woocommerce, 'template'], 1000); 50 | add_filter('comments_template', [$woocommerce, 'reviewsTemplate'], 11); 51 | } 52 | 53 | public function bindSetupAction() 54 | { 55 | if (doing_action('after_setup_theme')) { 56 | $this->app['woocommerce']->addThemeSupport(); 57 | } else { 58 | add_action('after_setup_theme', [$this->app['woocommerce'], 'addThemeSupport']); 59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------