From b7774d123f4f0c1efb6a61e830b6b1eb9b656ffe Mon Sep 17 00:00:00 2001 From: vienthuong Date: Sun, 23 May 2021 16:25:22 +0700 Subject: [PATCH] Release v1.0.0 --- CHANGELOG_de-DE.md | 35 ++++--- CHANGELOG_en-GB.md | 7 ++ README.md | 3 +- composer.json | 6 +- src/Controller/CompareProductController.php | 12 +-- src/DependencyInjection/services.xml | 12 ++- src/FroshProductCompare.php | 2 +- src/Page/CompareProductPage.php | 5 +- src/Page/CompareProductPageLoader.php | 99 ++++++++----------- .../sw-property-multi-select/index.js | 25 +++++ src/Resources/app/administration/src/main.js | 5 +- .../storefront/js/frosh-product-compare.js | 2 +- .../app/storefront/src/scss/compare.scss | 3 +- src/Resources/config/config.xml | 13 +++ .../js/frosh-product-compare.js | 2 +- .../component/compare/content.html.twig | 21 +++- .../compare/offcanvas-product.html.twig | 20 ++++ .../product/card/compare-button.html.twig | 2 +- .../cross-selling/tabs.html.twig | 2 +- ...hCrossSellingProductListingSubscriber.php} | 69 +++++++------ .../FroshProductGatewayCriteriaSubscriber.php | 25 +++++ 21 files changed, 236 insertions(+), 134 deletions(-) create mode 100644 src/Resources/app/administration/src/extension/sw-settings/sw-property-multi-select/index.js rename src/Subscriber/{CrossSellingProductListingSubscriber.php => FroshCrossSellingProductListingSubscriber.php} (61%) create mode 100644 src/Subscriber/FroshProductGatewayCriteriaSubscriber.php diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index ded36fe..725c7b0 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -1,3 +1,10 @@ +# 1.1.0 - 6.4 kompatibel +- Shopware 6.4 Kompatibilität +- [Issue-14] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/14) Behebt, dass der Cross-Sellings-Vergleich nicht mit Produktvarianten funktioniert +- Anzeige des Wertes der Eigenschaftsoption der Variante in der Vergleichsliste +- [Issue-15] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/15) Neue Plugin-Konfiguration hinzugefügt, um zwischen "show all/selected properties" umzuschalten +- Kleinere Probleme beheben + # 1.0.6 - [Issue-11] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/11) Korrigieren Sie die Import-SCSS-Syntax. - Kleiner Refactor auf der `base.html.twig` des Plugins @@ -13,20 +20,18 @@ - Fix Cross-Selling funktioniert nach der Installation des Plugins nicht. # 1.0.2 - - Shopware 6.3 Kompatibilität -# 1.0.1 - Release on Shopware store as Frosh's plugin - -- Remove some redundant LoC. -- Add robot noindex meta on Compare page -- Update the plugin's name and add some store's requirements. - -# 1.0.0 - First release -- Add `Add to Compare` button in Product Card and Product detail. -- Add `Float button` on bottom left of the page with `added products counter`. -- Add `Products added to compare list offcanvas` when click `Float button`. -- Add `Compare page` to display Compare products table. Up to 4 products can be added into the list. -- Add `Print` button in Compare page to print the compare table. -- Add `Clear all` button to clear all products in the list. -- Add `Comparable switch` in `Administration > Product Detail > Cross-selling tab` that allow to display cross-selling products as a compare table. +# 1.0.1 - Freigabe im Shopware-Store als Plugin von Frosh +- Entfernen einiger überflüssiger LoC. +- Hinzufügen von robot noindex meta auf der Vergleichsseite +- Aktualisieren Sie den Namen des Plugins und fügen Sie einige Anforderungen des Shops hinzu. + +# 1.0.0 - Erste Freigabe +- Hinzufügen der Schaltfläche "Zum Vergleich hinzufügen" in der Produktkarte und im Produktdetail. +- Schaltfläche "Float" unten links auf der Seite mit Zähler für "hinzugefügte Produkte" hinzufügen. +- Hinzufügen von "Zur Vergleichsliste hinzugefügte Produkte außerhalb des Bildschirms", wenn Sie auf die Schaltfläche "Float" klicken. +- Fügen Sie "Vergleichsseite" hinzu, um die Tabelle "Produkte vergleichen" anzuzeigen. Es können bis zu 4 Produkte in die Liste aufgenommen werden. +- Schaltfläche "Drucken" auf der Vergleichsseite hinzufügen, um die Vergleichstabelle zu drucken. +- Schaltfläche "Alle löschen" hinzufügen, um alle Produkte in der Liste zu löschen. +- Schalter "Vergleichbar" in "Verwaltung > Produktdetails > Registerkarte "Cross-Selling"" hinzufügen, um Cross-Selling-Produkte als Vergleichstabelle anzuzeigen. diff --git a/CHANGELOG_en-GB.md b/CHANGELOG_en-GB.md index 0babb51..9c2ead6 100644 --- a/CHANGELOG_en-GB.md +++ b/CHANGELOG_en-GB.md @@ -1,3 +1,10 @@ +# 1.1.0 - 6.4 compatible +- Shopware 6.4 compatibility +- [Issue-14] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/14) Fixes cross-sellings compare does not work with product variants +- Show variant's property option value in compare list +- [Issue-15] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/15) Added a new plugin's config to toggle between show all/selected properties +- Fix minor issues + # 1.0.6 - [Issue-11] (https://github.com/FriendsOfShopware/FroshProductCompare/issues/11) Fix import scss syntax. - Small refactor on plugin's base.html.twig diff --git a/README.md b/README.md index 52cce35..59bc663 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,14 @@ http://shopware.thuong.lv/ - Add up to 4 products into compare list. - Show compare list separated in products compare page or in product detail's cross-selling tab. +- Compare all properties or selected properties, configurable in plugin's config - Read CHANGELOG for detail. ## Requirements | Version | Requirements | |--------- |---------------------------- | -| 1.0.6 | Shopware 6.2 >= | +| 1.0.6 | Shopware 6.4 >= | ## License diff --git a/composer.json b/composer.json index 3e98afa..fb06d7a 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "frosh/product-compare", "description": "A Simple Product Compare plugin for Shopware 6", - "version": "1.0.6", + "version": "1.1.0", "type": "shopware-platform-plugin", "license": "MIT", "authors": [ @@ -15,8 +15,8 @@ } ], "require": { - "shopware/core": "^6.3", - "shopware/storefront": "^6.3" + "shopware/core": "^6.4", + "shopware/storefront": "^6.4" }, "extra": { "shopware-plugin-class": "Frosh\\FroshProductCompare\\FroshProductCompare", diff --git a/src/Controller/CompareProductController.php b/src/Controller/CompareProductController.php index e0bda2e..e91f0c9 100644 --- a/src/Controller/CompareProductController.php +++ b/src/Controller/CompareProductController.php @@ -16,14 +16,9 @@ */ class CompareProductController extends StorefrontController { - /** - * @var CompareProductPageLoader - */ - private $compareProductPageLoader; - /** - * @var GenericPageLoader - */ - private $genericPageLoader; + private CompareProductPageLoader $compareProductPageLoader; + + private GenericPageLoader $genericPageLoader; public function __construct( CompareProductPageLoader $compareProductPageLoader, @@ -56,7 +51,6 @@ public function comparePageContent(Request $request, SalesChannelContext $contex /** * @Route("/compare/offcanvas", name="frontend.compare.offcanvas", options={"seo"="false"}, methods={"POST"}, defaults={"XmlHttpRequest"=true}) - * */ public function offcanvas(Request $request, SalesChannelContext $context): Response { diff --git a/src/DependencyInjection/services.xml b/src/DependencyInjection/services.xml index 3f63f98..64ecf1a 100644 --- a/src/DependencyInjection/services.xml +++ b/src/DependencyInjection/services.xml @@ -16,10 +16,11 @@ - + + @@ -31,10 +32,15 @@ - + - + + + + + + diff --git a/src/FroshProductCompare.php b/src/FroshProductCompare.php index b29831b..f9ed2b4 100644 --- a/src/FroshProductCompare.php +++ b/src/FroshProductCompare.php @@ -41,7 +41,7 @@ public function cleanRelatedData(): void /** @var Connection $connection */ $connection = $this->container->get(Connection::class); - $connection->exec(' + $connection->executeStatement(' DROP TABLE IF EXISTS frosh_cross_selling_comparable; '); } diff --git a/src/Page/CompareProductPage.php b/src/Page/CompareProductPage.php index 498b873..fc4b8d9 100644 --- a/src/Page/CompareProductPage.php +++ b/src/Page/CompareProductPage.php @@ -7,10 +7,7 @@ class CompareProductPage extends Page { - /** - * @var ProductListingResult - */ - protected $products; + protected ProductListingResult $products; public function getProducts(): ProductListingResult { diff --git a/src/Page/CompareProductPageLoader.php b/src/Page/CompareProductPageLoader.php index 038f6af..7a7ab3b 100644 --- a/src/Page/CompareProductPageLoader.php +++ b/src/Page/CompareProductPageLoader.php @@ -4,10 +4,8 @@ use Shopware\Core\Content\Product\Aggregate\ProductReview\ProductReviewCollection; use Shopware\Core\Content\Product\Aggregate\ProductReview\ProductReviewEntity; -use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent; -use Shopware\Core\Content\Product\Events\ProductListingResultEvent; +use Shopware\Core\Content\Product\Cart\ProductGateway; use Shopware\Core\Content\Product\ProductCollection; -use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingLoader; use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult; use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity; use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection; @@ -24,6 +22,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter; use Shopware\Core\Framework\Uuid\Uuid; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Shopware\Core\System\SystemConfig\SystemConfigService; use Shopware\Storefront\Page\GenericPageLoaderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; @@ -32,39 +31,33 @@ class CompareProductPageLoader { const MAX_COMPARE_PRODUCT_ITEMS = 4; - /** - * @var GenericPageLoaderInterface - */ - private $genericLoader; - /** - * @var ProductListingLoader - */ - private $productListingLoader; + private GenericPageLoaderInterface $genericLoader; - /** - * @var EventDispatcherInterface - */ - private $eventDispatcher; - /** - * @var EntityRepositoryInterface - */ - private $productReviewRepository; + private ProductGateway $productGateway; + + private EventDispatcherInterface $eventDispatcher; + + private EntityRepositoryInterface $productReviewRepository; + + private SystemConfigService $systemConfigService; public function __construct( - ProductListingLoader $productListingLoader, + ProductGateway $productGateway, GenericPageLoaderInterface $genericLoader, EventDispatcherInterface $eventDispatcher, - EntityRepositoryInterface $productReviewRepository + EntityRepositoryInterface $productReviewRepository, + SystemConfigService $systemConfigService ) { - $this->productListingLoader = $productListingLoader; + $this->productGateway = $productGateway; $this->genericLoader = $genericLoader; $this->eventDispatcher = $eventDispatcher; $this->productReviewRepository = $productReviewRepository; + $this->systemConfigService = $systemConfigService; } public function loadPreview(array $productIds, Request $request, SalesChannelContext $salesChannelContext): CompareProductPage { - $productIds = array_filter($productIds, function ($id) { + $productIds = array_filter(array_slice($productIds, 0, self::MAX_COMPARE_PRODUCT_ITEMS), function ($id) { return Uuid::isValid($id); }); @@ -80,7 +73,7 @@ public function loadPreview(array $productIds, Request $request, SalesChannelCon $criteria = new Criteria(); $criteria->setIds($productIds)->setLimit(self::MAX_COMPARE_PRODUCT_ITEMS); - $products = $this->productListingLoader->load($criteria, $salesChannelContext); + $products = $this->productGateway->get($productIds, $salesChannelContext); $result = ProductListingResult::createFrom($products); @@ -110,31 +103,27 @@ public function load(array $productIds, Request $request, SalesChannelContext $s return $page; } - $criteria = $this->getCompareProductListCriteria($productIds); - - $this->eventDispatcher->dispatch( - new ProductListingCriteriaEvent($request, $criteria, $salesChannelContext) - ); - - $products = $this->productListingLoader->load($criteria, $salesChannelContext); + $products = $this->productGateway->get($productIds, $salesChannelContext); $result = ProductListingResult::createFrom($products); - $result = $this->loadProductCompareData($result, $salesChannelContext->getContext()); - - $this->eventDispatcher->dispatch( - new ProductListingResultEvent($request, $result, $salesChannelContext) - ); + $result = $this->loadProductCompareData($result, $salesChannelContext); $page->setProducts($result); return $page; } - private function sortProperties(SalesChannelProductEntity $product): PropertyGroupCollection + private function sortProperties(SalesChannelProductEntity $product, array $selectedProperties): PropertyGroupCollection { $properties = $product->getProperties(); + if (!empty($selectedProperties)) { + $properties = $properties->filter(function (PropertyGroupOptionEntity $property) use ($selectedProperties) { + return in_array($property->getGroupId(), $selectedProperties); + }); + } + if ($properties === null) { return new PropertyGroupCollection(); } @@ -169,29 +158,11 @@ private function sortProperties(SalesChannelProductEntity $product): PropertyGro return $propertyGroupCollection; } - public function getCompareProductListCriteria(array $productIds): Criteria - { - $criteria = new Criteria(); - $criteria->setIds($productIds) - ->addAssociation('media') - ->addAssociation('prices') - ->addAssociation('manufacturer') - ->addAssociation('manufacturer.media') - ->addAssociation('cover') - ->addAssociation('options.group') - ->addAssociation('properties.group') - ->addAssociation('properties.media') - ->addAssociation('mainCategories.category') - ->setLimit(self::MAX_COMPARE_PRODUCT_ITEMS); - - return $criteria; - } - private function loadProductReviews(array $productIds, Context $context): EntityCollection { $criteria = new Criteria(); $criteria->addAggregation(new CountAggregation('count', 'id')); - $criteria->addFilter(new EqualsFilter('status', true)); + $criteria->addFilter(new EqualsFilter('status', true)); $criteria->addFilter( new MultiFilter(MultiFilter::CONNECTION_OR, [ new EqualsAnyFilter('product.id', $productIds), @@ -202,9 +173,19 @@ private function loadProductReviews(array $productIds, Context $context): Entity return $this->productReviewRepository->search($criteria, $context)->getEntities(); } - public function loadProductCompareData(ProductListingResult $products, Context $context): ProductListingResult + public function loadProductCompareData(ProductListingResult $products, SalesChannelContext $context): ProductListingResult { - $productReviews = $this->loadProductReviews($products->getIds(), $context); + $productReviews = $this->loadProductReviews($products->getIds(), $context->getContext()); + + $selectedProperties = []; + $showSelectedProperties = $this->systemConfigService->getBool('FroshProductCompare.config.showSelectedProperties', $context->getSalesChannelId()); + + if ($showSelectedProperties) { + $selectedProperties = $this->systemConfigService->get('FroshProductCompare.config.selectedProperties', $context->getSalesChannelId()); + $selectedProperties = array_map(function ($property) { + return $property['id']; + }, $selectedProperties); + } /** @var SalesChannelProductEntity $product */ foreach ($products as $product) { @@ -220,7 +201,7 @@ public function loadProductCompareData(ProductListingResult $products, Context $ } }); - $sortedProperties = $this->sortProperties($product); + $sortedProperties = $this->sortProperties($product, $selectedProperties); $product->setSortedProperties($sortedProperties); } diff --git a/src/Resources/app/administration/src/extension/sw-settings/sw-property-multi-select/index.js b/src/Resources/app/administration/src/extension/sw-settings/sw-property-multi-select/index.js new file mode 100644 index 0000000..c949996 --- /dev/null +++ b/src/Resources/app/administration/src/extension/sw-settings/sw-property-multi-select/index.js @@ -0,0 +1,25 @@ +const { Component } = Shopware; +const { Criteria, EntityCollection } = Shopware.Data; + +Component.extend('sw-property-multi-select', 'sw-entity-multi-select', { + props: { + entityCollection: { + type: Array, + required: true, + default() { + return new EntityCollection( + '/property_group', + 'property_group', + Shopware.Context.api, + new Criteria(1, this.resultLimit) + ); + } + }, + + entityName: { + type: String, + required: false, + default: 'property_group' + }, + }, +}); diff --git a/src/Resources/app/administration/src/main.js b/src/Resources/app/administration/src/main.js index 4c32926..9430acb 100644 --- a/src/Resources/app/administration/src/main.js +++ b/src/Resources/app/administration/src/main.js @@ -1,14 +1,15 @@ import './extension/sw-product/component/sw-product-cross-selling-form'; import './extension/sw-product/view/sw-product-detail-cross-selling'; import './extension/sw-product/page/sw-product-detail'; +import './extension/sw-settings/sw-property-multi-select'; Shopware.Module.register('frosh-product-compare', { type: 'plugin', name: 'FroshProductCompare', title: 'frosh-product-compare.generalInformation.mainMenuItemGeneral', description: 'frosh-product-compare.generalInformation.descriptionTextModule', - version: '1.0.1', - targetVersion: '1.0.1', + version: '1.1.0', + targetVersion: '1.1.0', color: '#9AA8B5', icon: 'default-shopping-paper-bag' }); diff --git a/src/Resources/app/storefront/dist/storefront/js/frosh-product-compare.js b/src/Resources/app/storefront/dist/storefront/js/frosh-product-compare.js index 49612f8..9b5097d 100644 --- a/src/Resources/app/storefront/dist/storefront/js/frosh-product-compare.js +++ b/src/Resources/app/storefront/dist/storefront/js/frosh-product-compare.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["frosh-product-compare"],{SNxI:function(t,e,n){"use strict";n.r(e);var o=n("FGIj"),r=n("Cxgn"),i=n("k8s9"),a=n("u0Tz"),s=n("3rxU");function u(t,e){for(var n=0;nthis.maximumCompareProducts||(e.push(t),this.persist(e),0))}},{key:"remove",value:function(t){var e=this.getAddedProductsList(),n=e.indexOf(t);return-1!==n&&(e.splice(n,1),this.persist(e),!0)}},{key:"persist",value:function(t){s.a.setItem(this.key,JSON.stringify(t)),document.$emitter.publish("changedProductCompare",{products:t})}},{key:"clear",value:function(){s.a.setItem(this.key,null)}},{key:"_checkCompareProductStorage",value:function(t){return t.length<=this.maximumCompareProducts}}],(n=null)&&u(e.prototype,n),o&&u(e,o),t}();c(l,"key","compare-widget-added-products"),c(l,"maximumCompareProducts",4);var f=l;function d(t){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function p(t,e){for(var n=0;n input[name=_csrf_token]").value,a.a.create(this.el),this._client.post(window.router["frontend.compare.content"],JSON.stringify(e),(function(e){a.a.remove(t.el),t.renderCompareProducts(e),t.$emitter.publish("insertStoredContent",{response:e})}))}},{key:"renderCompareProducts",value:function(t){this.el.querySelector(".compare-product-content").innerHTML=t,r.a.initializePlugin("AddToCompareButton",".compare-item-remove-button"),r.a.initializePlugin("AddToCart",".buy-widget")}},{key:"_registerEvents",value:function(){var t=this;document.$emitter.subscribe("removeCompareProduct",(function(e){var n=t.el.querySelector("table"),o=n.rows;if(2===n.querySelectorAll("thead tr td").length)return n.style.display="none",f.clear(),void t.insertStoredContent();for(var r=0;r-1}},{key:"_checkAddedProduct",value:function(){var t=this.options,e=t.productId,n=t.addedText,o=t.isAddedToCompareClass;this._isAddedProduct(e)&&n&&(this._toggleText(this.el,n),this.el.classList.add(o))}},{key:"_toggleText",value:function(t,e){this.options.showIconOnly||(t.textContent=e)}},{key:"_registerCompareButtonSelection",value:function(){var t=this,e=this.el,n=this.options,o=n.isAddedToCompareClass,r=n.productId;e.addEventListener("click",(function(){try{e.classList.contains(o)?(t._handleBeforeRemove(),f.remove(r)):f.add(r)&&t._handleBeforeAdd()}catch(t){f.clear()}}))}},{key:"_handleBeforeRemove",value:function(){var t=this.options,e=t.defaultText,n=t.isAddedToCompareClass,o={productId:t.productId};this.el.closest("td")?o.productRow=this.el.closest("td").cellIndex:this.el.closest(".offcanvas-comparison-item")?this.el.closest(".offcanvas-comparison-item").style.display="none":(this._toggleText(this.el,e),this.el.classList.remove(n)),document.$emitter.publish(this.REMOVE_COMPARE_PRODUCT_EVENT,{product:o})}},{key:"_handleBeforeAdd",value:function(){var t=this.options,e=t.addedText,n=t.isAddedToCompareClass;this._toggleText(this.el,e),this.el.classList.add(n)}},{key:"_registerEvents",value:function(){var t=this;this._registerCompareButtonSelection();var e=this.options,n=e.productId,o=e.isAddedToCompareClass,r=e.defaultText;document.$emitter.subscribe(this.REMOVE_COMPARE_PRODUCT_EVENT,(function(e){e.detail.product.productId===n&&(t._toggleText(t.el,r),t.el.classList.remove(o))}))}}])&&g(n.prototype,o),r&&g(n,r),e}(o.a);P={isAddedToCompareClass:"is-added-to-compare"},(k="options")in(w=S)?Object.defineProperty(w,k,{value:P,enumerable:!0,configurable:!0,writable:!0}):w[k]=P;var E=n("41MI"),T=n("lpb5"),L=n("2Jwc");function A(t){return(A="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function j(t,e){for(var n=0;n=f.maximumCompareProducts&&new L.a(t.options.maximumNumberCompareProductsText).open()})),document.$emitter.subscribe("changedProductCompare",(function(e){t._updateButtonCounter(e.detail.products)})),document.addEventListener("scroll",this._debouncedOnScroll,!1),new MutationObserver(this._addBodyPadding.bind(this)).observe(document.body,{attributes:!0,attributeFilter:["style"]})}},{key:"_addBodyPadding",value:function(){this._button.style.bottom="calc(".concat(this._defaultPadding," + ").concat(document.body.style.paddingBottom||"0px",")")}},{key:"_openOffcanvas",value:function(){var t=this,e={productIds:f.getAddedProductsList(),_csrf_token:this.el.querySelector("input[name=_csrf_token]").value};T.a.open(window.router["frontend.compare.offcanvas"],JSON.stringify(e),(function(e){t.$emitter.publish("insertStoredContent",{response:e})}))}}])&&j(n.prototype,o),r&&j(n,r),e}(o.a);!function(t,e,n){e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n}(I,"options",{buttonSelector:".js-compare-float-button"});var N=window.PluginManager;N.register("AddToCompareButton",S,"[data-add-to-compare-button]"),N.register("CompareWidget",b,"[data-compare-widget]"),N.register("CompareFloat",I,"[data-compare-float]")},bK22:function(t,e,n){"use strict";n.d(e,"a",(function(){return d})),n.d(e,"b",(function(){return p}));var o=n("41MI"),r=n("+F6M"),i=n("KeF5"),a=n("ERap");function s(t){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){for(var n=0;n0}},{key:"_openOffcanvas",value:function(t,e){setTimeout((function(){i.c.create((function(){t.classList.add("is-open"),window.history.pushState("offcanvas-open",""),"function"==typeof e&&e()}))}),75)}},{key:"_registerEvents",value:function(t,e){var n=this,r=o.a.isTouchDevice()?"touchstart":"click";if(t){document.addEventListener(i.a.ON_CLICK,(function t(){n.close(e),document.removeEventListener(i.a.ON_CLICK,t)}))}window.addEventListener("popstate",this.close.bind(this,e),{once:!0});var s=document.querySelectorAll(".".concat("js-offcanvas-close"));a.a.iterate(s,(function(t){return t.addEventListener(r,n.goBackInHistory.bind(n))}))}},{key:"_removeExistingOffCanvas",value:function(){var t=this.getOffCanvas();return a.a.iterate(t,(function(t){return t.remove()}))}},{key:"_getPositionClass",value:function(t){return"is-".concat(t)}},{key:"_createOffCanvas",value:function(t,e,n){var o=document.createElement("div");if(o.classList.add("offcanvas"),o.classList.add(this._getPositionClass(t)),!0===e&&o.classList.add("is-fullwidth"),n){var r=s(n);if("string"===r)o.classList.add(n);else{if(!Array.isArray(n))throw new Error('The type "'.concat(r,'" is not supported. Please pass an array or a string.'));n.forEach((function(t){o.classList.add(t)}))}}return document.body.appendChild(o),o}}]),t}(),d=Object.freeze(new f),p=function(){function t(){u(this,t)}return l(t,null,[{key:"open",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"left",o=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:350,i=arguments.length>5&&void 0!==arguments[5]&&arguments[5],a=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"";d.open(t,e,n,o,r,i,a)}},{key:"setContent",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:350;d.setContent(t,e,n)}},{key:"setAdditionalClassName",value:function(t){d.setAdditionalClassName(t)}},{key:"close",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:350;d.close(t)}},{key:"exists",value:function(){return d.exists()}},{key:"getOffCanvas",value:function(){return d.getOffCanvas()}},{key:"REMOVE_OFF_CANVAS_DELAY",value:function(){return 350}}]),t}()},lpb5:function(t,e,n){"use strict";n.d(e,"a",(function(){return p}));var o=n("bK22"),r=n("k8s9"),i=n("5lm9");function a(t){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function s(t,e){for(var n=0;n0&&void 0!==arguments[0]&&arguments[0],e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"left",i=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:o.b.REMOVE_OFF_CANVAS_DELAY(),s=arguments.length>6&&void 0!==arguments[6]&&arguments[6],u=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";if(!t)throw new Error("A url must be given!");o.a._removeExistingOffCanvas();var c=o.a._createOffCanvas(r,s,u);this.setContent(t,e,n,i,a),o.a._openOffcanvas(c)}},{key:"setContent",value:function(t,n,o,a,s){var u=this,f=new r.a;c(l(e),"setContent",this).call(this,'
'.concat(i.a.getTemplate(),"
"),a,s),d&&d.abort();var p=function(t){c(l(e),"setContent",u).call(u,t,a,s),"function"==typeof o&&o(t)};d=n?f.post(t,n,e.executeCallback.bind(this,p)):f.get(t,e.executeCallback.bind(this,p))}},{key:"executeCallback",value:function(t,e){"function"==typeof t&&t(e),window.PluginManager.initializePlugins()}}],(a=null)&&s(n.prototype,a),p&&s(n,p),e}(o.b)}},[["SNxI","runtime","vendor-node","vendor-shared"]]]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([["frosh-product-compare"],{SNxI:function(t,e,n){"use strict";n.r(e);var o=n("FGIj"),r=n("Cxgn"),i=n("k8s9"),a=n("u0Tz"),s=n("3rxU");function u(t,e){for(var n=0;nthis.maximumCompareProducts||(e.push(t),this.persist(e),0))}},{key:"remove",value:function(t){var e=this.getAddedProductsList(),n=e.indexOf(t);return-1!==n&&(e.splice(n,1),this.persist(e),!0)}},{key:"persist",value:function(t){s.a.setItem(this.key,JSON.stringify(t)),document.$emitter.publish("changedProductCompare",{products:t})}},{key:"clear",value:function(){s.a.setItem(this.key,null)}},{key:"_checkCompareProductStorage",value:function(t){return t.length<=this.maximumCompareProducts}}],(n=null)&&u(e.prototype,n),o&&u(e,o),t}();c(l,"key","compare-widget-added-products"),c(l,"maximumCompareProducts",4);var f=l;function d(t){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function p(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function y(t,e){for(var n=0;n input[name=_csrf_token]").value,a.a.create(this.el),this._client.post(window.router["frontend.compare.content"],JSON.stringify(e),(function(e){a.a.remove(t.el),t.renderCompareProducts(e),t.$emitter.publish("insertStoredContent",{response:e})}))}},{key:"renderCompareProducts",value:function(t){this.el.querySelector(".compare-product-content").innerHTML=t,r.a.initializePlugin("AddToCompareButton",".compare-item-remove-button"),r.a.initializePlugin("AddToCart",".buy-widget")}},{key:"_registerEvents",value:function(){var t=this;document.$emitter.subscribe("removeCompareProduct",(function(e){var n=t.el.querySelector("table"),o=n.rows;if(2===n.querySelectorAll("thead tr td").length)return n.style.display="none",f.clear(),void t.insertStoredContent();for(var r=0;r-1}},{key:"_checkAddedProduct",value:function(){var t=this.options,e=t.productId,n=t.addedText,o=t.isAddedToCompareClass;this._isAddedProduct(e)&&n&&(this._toggleText(this.el,n),this.el.classList.add(o))}},{key:"_toggleText",value:function(t,e){this.options.showIconOnly||(t.textContent=e)}},{key:"_registerCompareButtonSelection",value:function(){var t=this,e=this.el,n=this.options,o=n.isAddedToCompareClass,r=n.productId;e.addEventListener("click",(function(){try{e.classList.contains(o)?(t._handleBeforeRemove(),f.remove(r)):f.add(r)&&t._handleBeforeAdd()}catch(t){f.clear()}}))}},{key:"_handleBeforeRemove",value:function(){var t=this.options,e=t.defaultText,n=t.isAddedToCompareClass,o={productId:t.productId};this.el.closest("td")?o.productRow=this.el.closest("td").cellIndex:this.el.closest(".offcanvas-comparison-item")?this.el.closest(".offcanvas-comparison-item").style.display="none":(this._toggleText(this.el,e),this.el.classList.remove(n)),document.$emitter.publish(this.REMOVE_COMPARE_PRODUCT_EVENT,{product:o})}},{key:"_handleBeforeAdd",value:function(){var t=this.options,e=t.addedText,n=t.isAddedToCompareClass;this._toggleText(this.el,e),this.el.classList.add(n)}},{key:"_registerEvents",value:function(){var t=this;this._registerCompareButtonSelection();var e=this.options,n=e.productId,o=e.isAddedToCompareClass,r=e.defaultText;document.$emitter.subscribe(this.REMOVE_COMPARE_PRODUCT_EVENT,(function(e){e.detail.product.productId===n&&(t._toggleText(t.el,r),t.el.classList.remove(o))}))}}])&&C(n.prototype,o),r&&C(n,r),e}(o.a);E={isAddedToCompareClass:"is-added-to-compare"},(S="options")in(P=T)?Object.defineProperty(P,S,{value:E,enumerable:!0,configurable:!0,writable:!0}):P[S]=E;var L=n("41MI"),A=n("lpb5"),j=n("2Jwc");function x(t){return(x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function R(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function B(t,e){for(var n=0;n=f.maximumCompareProducts&&new j.a(t.options.maximumNumberCompareProductsText).open()})),document.$emitter.subscribe("changedProductCompare",(function(e){t._updateButtonCounter(e.detail.products)})),document.addEventListener("scroll",this._debouncedOnScroll,!1),new MutationObserver(this._addBodyPadding.bind(this)).observe(document.body,{attributes:!0,attributeFilter:["style"]})}},{key:"_addBodyPadding",value:function(){this._button.style.bottom="calc(".concat(this._defaultPadding," + ").concat(document.body.style.paddingBottom||"0px",")")}},{key:"_openOffcanvas",value:function(){var t=this,e={productIds:f.getAddedProductsList(),_csrf_token:this.el.querySelector("input[name=_csrf_token]").value};A.a.open(window.router["frontend.compare.offcanvas"],JSON.stringify(e),(function(e){t.$emitter.publish("insertStoredContent",{response:e})}))}}])&&B(n.prototype,o),r&&B(n,r),e}(o.a);!function(t,e,n){e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n}(q,"options",{buttonSelector:".js-compare-float-button"});var $=window.PluginManager;$.register("AddToCompareButton",T,"[data-add-to-compare-button]"),$.register("CompareWidget",m,"[data-compare-widget]"),$.register("CompareFloat",q,"[data-compare-float]")},bK22:function(t,e,n){"use strict";n.d(e,"a",(function(){return h})),n.d(e,"b",(function(){return v}));var o=n("41MI"),r=n("+F6M"),i=n("KeF5"),a=n("ERap");function s(t){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){for(var n=0;n0}},{key:"_openOffcanvas",value:function(t,e){setTimeout((function(){i.c.create((function(){t.classList.add(d),window.history.pushState("offcanvas-open",""),"function"==typeof e&&e()}))}),75)}},{key:"_registerEvents",value:function(t,e){var n=this,r=o.a.isTouchDevice()?"touchstart":"click";if(t){document.addEventListener(i.a.ON_CLICK,(function t(){n.close(e),document.removeEventListener(i.a.ON_CLICK,t)}))}window.addEventListener("popstate",this.close.bind(this,e),{once:!0});var s=document.querySelectorAll(".".concat("js-offcanvas-close"));a.a.iterate(s,(function(t){return t.addEventListener(r,n.goBackInHistory.bind(n))}))}},{key:"_removeExistingOffCanvas",value:function(){var t=this.getOffCanvas();return a.a.iterate(t,(function(t){return t.remove()}))}},{key:"_getPositionClass",value:function(t){return"is-".concat(t)}},{key:"_createOffCanvas",value:function(t,e,n){var o=document.createElement("div");if(o.classList.add(f),o.classList.add(this._getPositionClass(t)),!0===e&&o.classList.add("is-fullwidth"),n){var r=s(n);if("string"===r)o.classList.add(n);else{if(!Array.isArray(n))throw new Error('The type "'.concat(r,'" is not supported. Please pass an array or a string.'));n.forEach((function(t){o.classList.add(t)}))}}return document.body.appendChild(o),o}}]),t}(),h=Object.freeze(new y),v=function(){function t(){u(this,t)}return l(t,null,[{key:"open",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"left",o=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:p,i=arguments.length>5&&void 0!==arguments[5]&&arguments[5],a=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"";h.open(t,e,n,o,r,i,a)}},{key:"setContent",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:p;h.setContent(t,e,n)}},{key:"setAdditionalClassName",value:function(t){h.setAdditionalClassName(t)}},{key:"close",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:p;h.close(t)}},{key:"exists",value:function(){return h.exists()}},{key:"getOffCanvas",value:function(){return h.getOffCanvas()}},{key:"REMOVE_OFF_CANVAS_DELAY",value:function(){return p}}]),t}()},lpb5:function(t,e,n){"use strict";n.d(e,"a",(function(){return y}));var o=n("bK22"),r=n("k8s9"),i=n("5lm9");function a(t){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function u(t,e){for(var n=0;n0&&void 0!==arguments[0]&&arguments[0],e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"left",i=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:o.b.REMOVE_OFF_CANVAS_DELAY(),s=arguments.length>6&&void 0!==arguments[6]&&arguments[6],u=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";if(!t)throw new Error("A url must be given!");o.a._removeExistingOffCanvas();var c=o.a._createOffCanvas(r,s,u);this.setContent(t,e,n,i,a),o.a._openOffcanvas(c)}},{key:"setContent",value:function(t,n,o,a,s){var u=this,c=new r.a;l(f(e),"setContent",this).call(this,'
'.concat(i.a.getTemplate(),"
"),a,s),p&&p.abort();var d=function(t){l(f(e),"setContent",u).call(u,t,a,s),"function"==typeof o&&o(t)};p=n?c.post(t,n,e.executeCallback.bind(this,d)):c.get(t,e.executeCallback.bind(this,d))}},{key:"executeCallback",value:function(t,e){"function"==typeof t&&t(e),window.PluginManager.initializePlugins()}}],(a=null)&&u(n.prototype,a),y&&u(n,y),e}(o.b)}},[["SNxI","runtime","vendor-node","vendor-shared"]]]); \ No newline at end of file diff --git a/src/Resources/app/storefront/src/scss/compare.scss b/src/Resources/app/storefront/src/scss/compare.scss index 5bda595..16a6907 100644 --- a/src/Resources/app/storefront/src/scss/compare.scss +++ b/src/Resources/app/storefront/src/scss/compare.scss @@ -55,7 +55,7 @@ } } } - + .table-bordered thead { td { border-bottom-width: 1px; @@ -98,6 +98,7 @@ position: absolute; top: 0; right: 0; + z-index: 99; .icon { color: $white; diff --git a/src/Resources/config/config.xml b/src/Resources/config/config.xml index 4c37e36..42d7df5 100644 --- a/src/Resources/config/config.xml +++ b/src/Resources/config/config.xml @@ -12,5 +12,18 @@ false + + showSelectedProperties + bordered + + + false + + + + selectedProperties + + + diff --git a/src/Resources/public/administration/js/frosh-product-compare.js b/src/Resources/public/administration/js/frosh-product-compare.js index 1190659..e977b81 100644 --- a/src/Resources/public/administration/js/frosh-product-compare.js +++ b/src/Resources/public/administration/js/frosh-product-compare.js @@ -1 +1 @@ -(this.webpackJsonp=this.webpackJsonp||[]).push([["frosh-product-compare"],{"+hPK":function(s,e,o){var r=o("ZQc6");"string"==typeof r&&(r=[[s.i,r,""]]),r.locals&&(s.exports=r.locals);(0,o("SZ7m").default)("880dc976",r,!0,{})},"0Ld4":function(s,e){s.exports='{% block sw_prduct_cross_selling_form_active_field %}\n {% parent %}\n \n \n{% endblock %}\n\n{% block sw_prduct_cross_selling_form_limit_field %}\n \n \n{% endblock %}\n'},I0Vh:function(s,e){const{Component:o}=Shopware;o.override("sw-product-detail",{computed:{productCriteria(){const s=this.$super("productCriteria");return s.addAssociation("crossSellings.crossSellingComparable"),s}}})},Ivmq:function(s,e){const{Component:o}=Shopware;o.override("sw-product-detail-cross-selling",{methods:{onAddCrossSelling(){const s=this.repositoryFactory.create(this.product.crossSellings.entity,this.product.crossSellings.source);this.crossSelling=s.create(Shopware.Context.api);const e=this.repositoryFactory.create("frosh_cross_selling_comparable").create(Shopware.Context.api);e.isComparable=!1,this.crossSelling.productId=this.product.id,this.crossSelling.position=this.product.crossSellings.length+1,this.crossSelling.type="productStream",this.crossSelling.sortBy="name",this.crossSelling.sortDirection="ASC",this.crossSelling.limit=24,this.crossSelling.extensions.crossSellingComparable=e,this.product.crossSellings.push(this.crossSelling)}}})},XPv5:function(s,e,o){"use strict";o.r(e);var r=o("0Ld4"),i=o.n(r);o("+hPK");const{Component:t,Utils:l}=Shopware;t.override("sw-product-cross-selling-form",{template:i.a,data(){return{originalLimit:this.crossSelling.limit}},watch:{"crossSelling.extensions.crossSellingComparable.isComparable":{deep:!0,immediate:!0,handler(s){this.crossSelling.limit=s?4:this.originalLimit}}},created(){let s=l.get(this.crossSelling,"extensions.crossSellingComparable",null);s||(s=this.repositoryFactory.create("frosh_cross_selling_comparable").create(Shopware.Context.api),s.isComparable=!1,this.crossSelling.extensions.crossSellingComparable=s),l.get(this.crossSelling,"extensions.crossSellingComparable.isComparable",!1)&&(this.crossSelling.limit=4)}});o("Ivmq"),o("I0Vh");Shopware.Module.register("frosh-product-compare",{type:"plugin",name:"FroshProductCompare",title:"frosh-product-compare.generalInformation.mainMenuItemGeneral",description:"frosh-product-compare.generalInformation.descriptionTextModule",version:"1.0.1",targetVersion:"1.0.1",color:"#9AA8B5",icon:"default-shopping-paper-bag"})},ZQc6:function(s,e,o){}},[["XPv5","runtime","vendors-node"]]]); \ No newline at end of file +(this.webpackJsonp=this.webpackJsonp||[]).push([["frosh-product-compare"],{"+hPK":function(e,s,o){var r=o("Xo0n");"string"==typeof r&&(r=[[e.i,r,""]]),r.locals&&(e.exports=r.locals);(0,o("SZ7m").default)("22f4925f",r,!0,{})},"0Ld4":function(e,s){e.exports='{% block sw_prduct_cross_selling_form_active_field %}\n {% parent %}\n \n \n{% endblock %}\n\n{% block sw_prduct_cross_selling_form_limit_field %}\n \n \n{% endblock %}\n'},I0Vh:function(e,s){const{Component:o}=Shopware;o.override("sw-product-detail",{computed:{productCriteria(){const e=this.$super("productCriteria");return e.addAssociation("crossSellings.crossSellingComparable"),e}}})},Ivmq:function(e,s){const{Component:o}=Shopware;o.override("sw-product-detail-cross-selling",{methods:{onAddCrossSelling(){const e=this.repositoryFactory.create(this.product.crossSellings.entity,this.product.crossSellings.source);this.crossSelling=e.create(Shopware.Context.api);const s=this.repositoryFactory.create("frosh_cross_selling_comparable").create(Shopware.Context.api);s.isComparable=!1,this.crossSelling.productId=this.product.id,this.crossSelling.position=this.product.crossSellings.length+1,this.crossSelling.type="productStream",this.crossSelling.sortBy="name",this.crossSelling.sortDirection="ASC",this.crossSelling.limit=24,this.crossSelling.extensions.crossSellingComparable=s,this.product.crossSellings.push(this.crossSelling)}}})},XPv5:function(e,s,o){"use strict";o.r(s);var r=o("0Ld4"),t=o.n(r);o("+hPK");const{Component:i,Utils:n}=Shopware;i.override("sw-product-cross-selling-form",{template:t.a,data(){return{originalLimit:this.crossSelling.limit}},watch:{"crossSelling.extensions.crossSellingComparable.isComparable":{deep:!0,immediate:!0,handler(e){this.crossSelling.limit=e?4:this.originalLimit}}},created(){let e=n.get(this.crossSelling,"extensions.crossSellingComparable",null);e||(e=this.repositoryFactory.create("frosh_cross_selling_comparable").create(Shopware.Context.api),e.isComparable=!1,this.crossSelling.extensions.crossSellingComparable=e),n.get(this.crossSelling,"extensions.crossSellingComparable.isComparable",!1)&&(this.crossSelling.limit=4)}});o("Ivmq"),o("I0Vh"),o("Z1ZH");Shopware.Module.register("frosh-product-compare",{type:"plugin",name:"FroshProductCompare",title:"frosh-product-compare.generalInformation.mainMenuItemGeneral",description:"frosh-product-compare.generalInformation.descriptionTextModule",version:"1.1.0",targetVersion:"1.1.0",color:"#9AA8B5",icon:"default-shopping-paper-bag"})},Xo0n:function(e,s,o){},Z1ZH:function(e,s){const{Component:o}=Shopware,{Criteria:r,EntityCollection:t}=Shopware.Data;o.extend("sw-property-multi-select","sw-entity-multi-select",{props:{entityCollection:{type:Array,required:!0,default(){return new t("/property_group","property_group",Shopware.Context.api,new r(1,this.resultLimit))}},entityName:{type:String,required:!1,default:"property_group"}}})}},[["XPv5","runtime","vendors-node"]]]); \ No newline at end of file diff --git a/src/Resources/views/storefront/component/compare/content.html.twig b/src/Resources/views/storefront/component/compare/content.html.twig index ba5c65b..f2728b8 100644 --- a/src/Resources/views/storefront/component/compare/content.html.twig +++ b/src/Resources/views/storefront/component/compare/content.html.twig @@ -80,7 +80,26 @@ {% endblock %} - {{ product.translated.name }} + {{ name|u.truncate(60) }} + {% if product.variation %} + {% block component_product_box_variant_characteristics %} +
+
+ {% for variation in product.variation %} + {{ variation.group }}: + + {{ variation.option }} + + + {% if product.variation|last != variation %} + {{ " | " }} + {% endif %} + {% endfor %} +
+
+ {% endblock %} + {% endif %} +
{% sw_include '@Storefront/storefront/component/product/card/action.html.twig' %}
diff --git a/src/Resources/views/storefront/component/compare/offcanvas-product.html.twig b/src/Resources/views/storefront/component/compare/offcanvas-product.html.twig index c2d74e5..e5c9f62 100644 --- a/src/Resources/views/storefront/component/compare/offcanvas-product.html.twig +++ b/src/Resources/views/storefront/component/compare/offcanvas-product.html.twig @@ -34,6 +34,7 @@ } %} {% endif %} + {% endblock %} @@ -51,6 +52,25 @@ title="{{ label }}"> {{ label|u.truncate(60) }} + + {% if product.variation %} + {% block component_product_box_variant_characteristics %} +
+
+ {% for variation in product.variation %} + {{ variation.group }}: + + {{ variation.option }} + + + {% if product.variation|last != variation %} + {{ " | " }} + {% endif %} + {% endfor %} +
+
+ {% endblock %} + {% endif %} {% endblock %} diff --git a/src/Resources/views/storefront/component/product/card/compare-button.html.twig b/src/Resources/views/storefront/component/product/card/compare-button.html.twig index 49dc346..162b65f 100644 --- a/src/Resources/views/storefront/component/product/card/compare-button.html.twig +++ b/src/Resources/views/storefront/component/product/card/compare-button.html.twig @@ -1,5 +1,5 @@ {% block frosh_product_compare_add_to_compare_box %} - {% set showIconOnly = shopware.config.FroshProductCompare.config is defined and shopware.config.FroshProductCompare.config.showIconOnly %} + {% set showIconOnly = config('FroshProductCompare.config.showIconOnly') %} {# @var product \Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity #}
{% set addToCompareOptions = { diff --git a/src/Resources/views/storefront/page/product-detail/cross-selling/tabs.html.twig b/src/Resources/views/storefront/page/product-detail/cross-selling/tabs.html.twig index 111c130..9ad3bb1 100644 --- a/src/Resources/views/storefront/page/product-detail/cross-selling/tabs.html.twig +++ b/src/Resources/views/storefront/page/product-detail/cross-selling/tabs.html.twig @@ -59,7 +59,7 @@ element: { 'data': { 'products': { - elements: item.getProducts() + elements: item.products } }, type: 'product-slider' diff --git a/src/Subscriber/CrossSellingProductListingSubscriber.php b/src/Subscriber/FroshCrossSellingProductListingSubscriber.php similarity index 61% rename from src/Subscriber/CrossSellingProductListingSubscriber.php rename to src/Subscriber/FroshCrossSellingProductListingSubscriber.php index c2ec899..b5f8608 100644 --- a/src/Subscriber/CrossSellingProductListingSubscriber.php +++ b/src/Subscriber/FroshCrossSellingProductListingSubscriber.php @@ -2,59 +2,52 @@ namespace Frosh\FroshProductCompare\Subscriber; -use Doctrine\DBAL\Connection; use Frosh\FroshProductCompare\CrossSellingComparable\CrossSellingComparableEntity; use Frosh\FroshProductCompare\Page\CompareProductPageLoader; +use Shopware\Core\Content\Product\Cart\ProductGateway; +use Shopware\Core\Content\Product\Events\ProductCrossSellingCriteriaEvent; +use Shopware\Core\Content\Product\Events\ProductCrossSellingIdsCriteriaEvent; +use Shopware\Core\Content\Product\Events\ProductCrossSellingsLoadedEvent; +use Shopware\Core\Content\Product\Events\ProductCrossSellingStreamCriteriaEvent; use Shopware\Core\Content\Product\ProductCollection; -use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingLoader; +use Shopware\Core\Content\Product\SalesChannel\CrossSelling\CrossSellingElement; use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult; use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter; use Shopware\Core\System\SalesChannel\SalesChannelContext; -use Shopware\Storefront\Page\Product\CrossSelling\CrossSellingElement; -use Shopware\Storefront\Page\Product\CrossSelling\CrossSellingLoadedEvent; -use Shopware\Storefront\Page\Product\CrossSelling\CrossSellingProductCriteriaEvent; -use Shopware\Storefront\Page\Product\CrossSelling\CrossSellingProductListCriteriaEvent; -use Shopware\Storefront\Page\Product\CrossSelling\CrossSellingProductStreamCriteriaEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class CrossSellingProductListingSubscriber implements EventSubscriberInterface +class FroshCrossSellingProductListingSubscriber implements EventSubscriberInterface { - /** - * @var CompareProductPageLoader - */ - private $compareProductPageLoader; + private CompareProductPageLoader $compareProductPageLoader; - /** - * @var ProductListingLoader - */ - private $productListingLoader; + private ProductGateway $productGateway; public function __construct( CompareProductPageLoader $compareProductPageLoader, - ProductListingLoader $productListingLoader + ProductGateway $productGateway ) { $this->compareProductPageLoader = $compareProductPageLoader; - $this->productListingLoader = $productListingLoader; + $this->productGateway = $productGateway; } public static function getSubscribedEvents() { return [ - CrossSellingProductStreamCriteriaEvent::class => [ + ProductCrossSellingStreamCriteriaEvent::class => [ ['handleCriteriaLoadedRequest', 201] ], - CrossSellingProductListCriteriaEvent::class => [ + ProductCrossSellingIdsCriteriaEvent::class => [ ['handleCriteriaLoadedRequest', 201] ], - CrossSellingLoadedEvent::class => [ + ProductCrossSellingsLoadedEvent::class => [ ['handleCrossSellingLoadedEvent', 201] ] ]; } - public function handleCriteriaLoadedRequest(CrossSellingProductCriteriaEvent $event): void + public function handleCriteriaLoadedRequest(ProductCrossSellingCriteriaEvent $event): void { $crossSelling = $event->getCrossSelling(); /** @var CrossSellingComparableEntity $crossSellingComparable */ @@ -77,12 +70,12 @@ public function handleCriteriaLoadedRequest(CrossSellingProductCriteriaEvent $ev ->addAssociation('properties.media') ->addAssociation('mainCategories.category') ->addFilter(new NotFilter(NotFilter::CONNECTION_AND, [new EqualsFilter('id', $crossSelling->getProductId())])) - ->setLimit(CompareProductPageLoader::MAX_COMPARE_PRODUCT_ITEMS - 1); + ->setLimit(CompareProductPageLoader::MAX_COMPARE_PRODUCT_ITEMS); } - public function handleCrossSellingLoadedEvent(CrossSellingLoadedEvent $event) + public function handleCrossSellingLoadedEvent(ProductCrossSellingsLoadedEvent $event) { - $crossSellings = $event->getCrossSellingResult(); + $crossSellings = $event->getCrossSellings(); $context = $event->getContext(); @@ -90,7 +83,6 @@ public function handleCrossSellingLoadedEvent(CrossSellingLoadedEvent $event) /** @var CrossSellingElement $crossSellingElement */ foreach ($crossSellings as $crossSellingElement) { - $crossSelling = $crossSellingElement->getCrossSelling(); /** @var CrossSellingComparableEntity $crossSellingComparable */ @@ -100,9 +92,23 @@ public function handleCrossSellingLoadedEvent(CrossSellingLoadedEvent $event) continue; } - $featureProduct = $this->getFeaturedProduct($crossSelling->getProductId(), $salesChannelContext); + $featureProductId = $crossSelling->getProductId(); + + /** @var SalesChannelProductEntity $product */ + foreach ($crossSellingElement->getProducts() as $product) { + if ($product->getParentId() === $featureProductId) { + // if there's at least a variant that in the compare list, remove container product from compare list + $featureProductId = null; + break; + } + } - $products = new ProductCollection([$featureProduct]); + $products = new ProductCollection(); + + if ($featureProductId) { + $featureProduct = $this->getFeaturedProduct($featureProductId, $salesChannelContext); + $products->add($featureProduct); + } $compareProducts = $crossSellingElement->getProducts(); @@ -118,10 +124,11 @@ public function handleCrossSellingLoadedEvent(CrossSellingLoadedEvent $event) private function getFeaturedProduct(string $productId, SalesChannelContext $context): SalesChannelProductEntity { - $criteria = $this->compareProductPageLoader->getCompareProductListCriteria([$productId]); + $products = $this->productGateway->get([$productId], $context); - $products = $this->productListingLoader->load($criteria, $context); + /** @var SalesChannelProductEntity $salesChannelProduct */ + $salesChannelProduct = $products->get($productId); - return $products->first(); + return $salesChannelProduct; } } diff --git a/src/Subscriber/FroshProductGatewayCriteriaSubscriber.php b/src/Subscriber/FroshProductGatewayCriteriaSubscriber.php new file mode 100644 index 0000000..dce8c82 --- /dev/null +++ b/src/Subscriber/FroshProductGatewayCriteriaSubscriber.php @@ -0,0 +1,25 @@ + [ + ['handleCriteriaLoadedRequest', 201] + ], + ]; + } + + public function handleCriteriaLoadedRequest(ProductGatewayCriteriaEvent $event): void + { + $criteria = $event->getCriteria(); + + $criteria->addAssociation('prices')->addAssociation('manufacturer'); + } +}