Coder Social home page Coder Social logo

magento2tweakwise's Introduction

Installation

Install package using composer

composer require tweakwise/magento2-tweakwise

This will install tweakwise/magento2-tweakwise and tweakwise/magento2-tweakwise-export

Enable module(s) and run installers

php bin/magento module:enable Tweakwise_Magento2TweakwiseExport Tweakwise_Magento2Tweakwise
php bin/magento setup:upgrade
php bin/magento setup:static-content:deploy

Configuration

When the extension is installed it is disabled by default. There are three different parts which can be enabled separately. Configuration can be found at Stores -> Configuration -> Catalog -> Tweakwise.

Rundown of all possible settings

Below is a rundown of all configuration options

General:

  1. Authentication key: This is used to communicate with tweakwise and determines your navigator instance, it should be the same as the key found in the navigator under Connectivity > End points.
  2. Server url: The url of the tweakwise server.
  3. Timeout: If tweakwise fails to respond after this time in seconds the request is aborted.

Layered Navigation (All settings depend on Enabled having value yes):

  1. Enabled: Use tweakwise results in navigation, if disabled the standard magento navigation is used. Don't disable the anchor tag on main categories, this causes al products to be shown. The anchor tag can be disabled on sub-categories.

  2. Hide facets with only one option: Given a result set from tweakwise in which a filter has only one option show that filter or not?

  3. Use default magento filter renderer: Use Magento standard filter templates or use templates bundled by the module. If you want to make full use of the features provided by this module then this should be set to No (i.e. make use of tweakwise template files).

  4. Filter form: This depends on 'Use default magento filter renderer' having value No. Render all filters in a form with filter buttons so that the user can select a set of filters and then navigate to the result instead of immediately navigating to the results when a filter is clicked.

  5. Filter url query parameters: Tweakwise filter urls will have all query parameters of the page in it so also the "cid" and utm_source parameters if present. You can determine in which way you want to filter these out (if any).

  6. Filter url query arguments: This depends on 'Filter url query parameters' having any value not equal to 'Dont Filter'. This field specifies which parameters should be removed from the tweakwise filter urls.

  7. Url strategy: Has two options Query parameters and Seo path slug. If query parameters is selected then the tweakwise filter urls (and thus your navigation urls) will be constructed as www.example.com/example-category?color=red.

    If Seo path slugs is selected the url is constructed as www.example.com/example-category/color/red.

Seo (All settings depend on Enabled having value yes)

  1. Enabled: use Seo options yes or no.
  2. Filter whitelist: A list of filters which should be indexable (all filters not selected here are not indexable). If a filter is marked as not indexable then its href attribute will be set to "#" its original url will be set in a data-seo-href attribute which will be used by javascript to navigate. Note that the category filter is always marked as indexable. This used to be a multiselect field containing magento attributes however tweakwise facilitates derived properties, these properties are not related to magento attributes and as such these filters would be not indexable. The field has changed to a comma separated text field so that these derived properties can be properly whitelisted.
  3. Max allowed facets: This combines with the Filter whitelist setting. Filters are indexable if and only if they are in the whitelist and the selected filter count does not go above max_allowed_facets. The reason this is an AND check is because otherwise indexation will still happen on the non whitelisted filters and it is unclear which url is present (an arbitrary amount of filters could be selected). Suppose max allowed facet is 1 and only "size" is in the whitelist. Then filter "color" with value "red" is not indexable (since "color" is not in the whitelist). If we now allow the size filter to still be indexable then url example.com/category/color/red/size/M would be indexable whereas example.com/category/color/red is not which is incorrect. This would lead to infinite crawling on filter urls which is undesirable

Autocomplete (All settings depend on Enabled having value yes)

  1. Enabled: Use tweakwise autocomplete results or not.
  2. Use Suggestions Autocomplete: Use new suggestion api (Yes) or use the standard autocomplete api (No)
  3. Show products *: Show product suggestions in autocomplete results.
  4. Show suggestions *: Show search suggestions in autocomplete results.
  5. Stay in category: Use the current category when getting autocomplete results.
  6. Maximum number of results *: At most this many autocomplete results will be show.
  • Hidden when Use Suggestions Autocomplete is enabled. These settings are not used in the new suggestions api. The new suggestions api is configured in tweakwise itself.

Search (All settings depend on Enabled having value yes)

  1. Enabled: Use tweakwise search of default magento search results
  2. Tweakwise search template: The tweakwise template to use for search results (this determines which filters are visible)
  3. Search language: This determines the language used by the store and is passed to tweakwise. Tweakwise uses this to determine word conjugations and also correct spelling errors when considering which results should be shown to the user. An example: suppose Language is set to 'Dutch' and the user types 'Bed' (which is the same in English, namely the place where one sleeps) then tweakwise might suggest 'Bedden' (this is plural for 'Beds') If Language is set to English then in the example above tweakwise might suggest 'Beds'.
  4. Searchbanners enabled: Show searchbanners in the search results. The searchbanners need to be configured in tweakwise.

Merchandising Builder

  1. Enabled: Use Merchandising Builder (Yes/No) This is only available if you use ajax filtering.
  2. Cookie name: The cookie which holds the tweakwise profile id, this cookie is (usually) set with a tracking script. The value of this cookie will be added to the tweakwise request, the response will contain a personalized sort order for that particular customer.

Note that Varnish must be enabled for this functionality to work correctly. Varnish is recommended by Magento anyway. The big advantage of Varnish Cache is of course the gain in speed. In addition, it ensures a lower load on the server and thus increases peak resistance. Using this feature means that all category pages have personalized content. As such, it is no longer possible cache navigation responses where this profile cookie name has been used. The product list is loaded via the merchandising builder only if the following conditions are met:

  1. Personal merchandising setting is enabled
  2. Cookie name setting has a value
  3. The user has the specified cookie with a non empty value.

When the product list is loaded in such a manner the result will not be cacheable. This has consequences for server load keep this in mind.

Recommendations

  1. Related products enabled: Replace magento native related products with tweakwise crosssell & upsell recommendations. Terminology is confusing since this is relevant for magento related products and not for magento crosssell products
  2. Default related products template: Which tweakwise recommendation template to use for related products. Only relevant when crosssell is enabled This can also be configured on a product and on a category. The template used is determined as follows: first check product for a configured template if not then check the product category for a template. If the category does not have a template configured then use the default.
  3. Default related group code: Only visible when Default crosssell template has value '- Group Code -'. Use this to specify the group of recommendations
  4. Upsell Enabled: Replace magento native upsell results with tweakwise crosssell & upsell recommendations.
  5. Default upsell template: Which template recommendation template to use for upsell products. Only relevant when upsell is enabled. This can also be configured on a product and on a category. The template used is determined as follows: first check product for a configured template if not then check the product category for a template. If the category does not have a template configured then use the default.
  6. Default upsell group code: Only visible when Default upsell template has value '- Group Code -'. Use this to specify the group of recommendations
  7. Featured products enabled: If yes then tweakwise can show featured products on category pages.
  8. Default Featured product template: The default template to use when rendering featured products. The template can also be set per category and falls back to this setting if not found on the category. The templates that can be selected correspond with the templates under 'recomendations->featured products' in tweakwise.
  9. Show Cross-sell items in the shoppingcart. Enables tweakwise cross-sell items in the shoppingcart. Magento shoppingcart crossell items should also be enabled under 'Stores->configuration->sales->checkout->Show Cross-sell items in the Shopping Cart'
  10. Default crosssell template. Which tweakwise recommendation template to use for shoppingcart crossell items. Only relevant when shoppingcart crosssell is enabled
  11. Default crosssell group code: Only visible when Default shoppincart crosssell template has value '- Group Code -'. Use this to specify the group of recommendations
  12. Crossell type: show crossell or featured products in shoppingcart
  13. Only show products from current category for featured products: Show product from current category in featured products.
  14. Limit group code recommendations: If group code is used for one/more recommendations, limit the total number of products returned. If empty or 0, all products are returned.

Support

For in depth support regarding configuration and all options tweakwise has to offer use the following links.

  1. Tweakwise support: https://www.tweakwise.com/support/
  2. Tweakwise api documentation: http://developers.tweakwise.com/
  3. General questions: https://www.tweakwise.com/contact/
  4. Security vulnerabilities: Send an email to [email protected], with the message that it's an security issue for the magento plugin

For feature requests we refer to the links above. For technical issues github is used. If you find a technical issue please create an issue on github and notify tweakwise via the links above. If you also happen to have the solution to that issue feel free to create a merge request.

Compatibility

We strive to remain compatible with all Magento 2.X versions and the latest 2.X-1 version where X is the highest Magento official 'sub' release. Currently X=4 hence we should be compatible with all 2.4 versions and the latest 2.3.x version. We do not actively drop support for versions below this range and will implement minor changes if that means we can remain compatible with versions below this range. That being said if we can do a massive simplification of code at the cost of dropping support for version 2.3.Y we will do so. We also refer to the magento software lifecycle: https://magento.com/sites/default/files/magento-software-lifecycle-policy.pdf. Note that 2.3 is End Of Life.

Contributors

If you want to create a pull request as a contributor, use the guidelines of semantic-release. semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes, and publishing the package. By adhering to the commit message format, a release is automatically created with the commit messages as release notes. Follow the guidelines as described in: https://github.com/semantic-release/semantic-release?tab=readme-ov-file#commit-message-format.

magento2tweakwise's People

Contributors

aadmathijssen avatar ah-net avatar beagon avatar binohartoko avatar bramstroker avatar dz-optimizers avatar edwinljacobs avatar eezeebee-ahuininga avatar evs-xsarus avatar fgruntjes avatar gdvisser avatar hnto avatar igorwulff avatar jansentjeu avatar jasperzeinstra avatar jeroenboersma avatar joost-florijn-kega avatar kaplansin avatar kpabbd avatar podenemus avatar poespas avatar remco-schouten avatar renehol avatar rofokken avatar sanderjongsma avatar semantic-release-bot avatar stijnbernards avatar swahjak avatar timoffmax avatar woutersteen avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

magento2tweakwise's Issues

Saving visual swatches failed

Issue Brief

Cannot save visual swatch attribute due to attribute source being set by tweakwise first before Magento swatches save plugin can modify the data after saving.
Disabling following observer, fixes the issue: https://github.com/EmicoEcommerce/Magento2Tweakwise/blob/master/etc/events.xml#L8-L13
This is due that the store id for the following source would be null and returns 1 (this is not default store id) and it will be used in following calls by magento and likewise in magento swatches module, when disabling the observer magento will set store id 0 when in the source then it will be also be getting store id 0 instead of 1 when it is enabled.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p1
  • Tweakwise Version: v5.0.9
  • Tweakwise Export Version: v5.0.3
  • Magento Deploy Mode: production & development
  • Third party modules:

Steps to reproduce

  1. Install Magento from master branch.
  2. Configure an visual swatch attribute like: image
  3. Save

Actual result

  1. Error message appears: "Warning: Undefined array key 5 in /app/xxxxx/vendor/magento/module-swatches/Model/Plugin/EavAttribute.php on line 241".

Expected result

List the expected results as a bullet list of expectations

  • Be able to save attribute options.

Store Code in url removed after ajax response

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p3
  • Tweakwise Version: 5.4.2
  • Tweakwise Export Version:
  • Magento Deploy Mode: (development)
  • Third party modules:

Steps to reproduce

1.Enable store code in url. confifg path : web/url/use_store
2. Navigate to a category page [domain]/[storecode]/[categorykey]
3. After getting response from this ajax call.
[domain]/[storecode]/tweakwise/ajax/navigation/?__tw_ajax_type=category&__tw_object_id=[objectId]&__tw_original_url=[originalUrl]&__tw_hash=[hash]

Actual result

1- storecode in url will be removed
[domain]/[categorykey]

Expected result

if Store code in url is enabled (web/url/use_store) store code in url should remain as it is in any condition.

Personal Merchandising

Issue Brief

The page is not being refreshed correctly and you can't use the toolbar.

If you enable form filter, there is no "on change" used, see:
https://github.com/EmicoEcommerce/Magento2Tweakwise/blob/master/view/frontend/web/js/navigation-form.js#L78-L84

Only change is being called in these two spots:
https://github.com/EmicoEcommerce/Magento2Tweakwise/blob/master/view/frontend/web/js/toolbar.js#L74
https://github.com/EmicoEcommerce/Magento2Tweakwise/blob/master/view/frontend/web/js/pm-page-reload.js#L35

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p5
  • Tweakwise Version: v5.8.1
  • Tweakwise Export Version: v6.0.0
  • Magento Deploy Mode: production/development
  • Third party modules:

Steps to reproduce

  1. Configure personal merchandising, enable ajax and filter form and use url strategy query param.
  2. Go to a category
  3. Click for going to the next page

Actual result

After step 2, there is no ajax request sent
After step 3, same thing

Expected result

ajax requests being sent

Category filter doesn't take into account applied filter

Usually, customers use a category tree in the filter bar.
Category tree works in some cases unexpectedly.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production

Pre-Conditions

  • ajax filter is enabled
  • category added to filter template as a tree
  • URL Strategy: Query params

Steps to reproduce

  1. visit the category overview page. e.g. https://example.con/category_path_url
  2. apply one filter (not category). e.g. https://example.con/category_path_url?filter_name=filter_value
  3. click on the category in the filter.

Actual result

  • filter from step 2 is not applied
  • customers are confused by the number of matched products. Because in step 2, the category tree shows a number of matched products with the applied filter.

Expected result

List the expected results as a bullet list of expectations

  • category tree filter takes into account applied filters.

Not needed AJAX call during first visit in category page category

Issue Brief

During first category page visit the AJAX call is sent (/tweakwise/ajax/navigation).

  • PHP Version: (8.1)
  • Magento Version: 2.4.5-p5
  • Tweakwise Version: 5.4.0 (however the same code that triggers the issue is on the latest version as well)
  • Magento Deploy Mode: production

Steps to reproduce

  1. Install Magento from master branch.
  2. Open home pape
  3. Open category page

Actual result
On the category page, the AJAX request (/tweakwise/ajax/navigation) is sent
Triggered by https://github.com/EmicoEcommerce/Magento2Tweakwise/blob/v5.8.1/view/frontend/web/js/navigation-form.js#L44

Expected result
On the category page, no AJAX request (/tweakwise/ajax/navigation) is sent until the filter is selected.

Reflected Cross-Site Scripting (XSS) Vulnerabilities

We found that some templates in the Tweakwise modules are vulnerable to XSS attacks.

Magento has strategies within the dev docs that could help prevent these attacks: https://devdocs.magento.com/guides/v2.3/extension-dev-guide/xss-protection.html

One found example is wrapping the $variables within $block->escapeHtml().

<input type="hidden" name="<?=$name?>" value="<?=$value?>">

Introduce some kind of automated linting (and fixing) to improve code quality

Issue Brief

In example; the following class has about 24 use statements, while only 2 are actually used. This is probably due to some refactoring at a point in time, but from a quality perspective this doesn't give me a lot of confidence. Not by any means saying that it lacks quality, I haven't gone into the code enough to say anything about it. But if such a simple task, that can be easily automated, is neglected it doesn't make me (the customer) very confident about the overall quality.

Also; adding code quality tools and linters makes it much easier to contribute.

Environment

Steps to reproduce

Actual result

Expected result

filters on PLP breaks addtocompare functionality

Issue Brief

When filtering products on PLP and then add a product to comparelist it redirects to "tweakwise/ajax/navigation/XXX" instead of redirecting to the page from which the current request was initiated "category page", For now removing "uenc" from addtocompare datapost will resolve it but not for long term.

Environment

  • PHP Version: (7.4|8.1|8.1)
  • Magento Version: 2.4.5-p2
  • Tweakwise Version: 5.5.2

When searching for empty query, a 400 bad request occurs

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.2
  • Magento Version: 2.4.6-p4
  • Tweakwise Version: 5,8.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production
  • Third party modules: n/a

Steps to reproduce

Start searching for suggestions, if this happens too soon, there is no search term. The request will be :

https://gateway.tweakwisenavigator.com/suggestions/090bb431?tn_q=&tn_cid=100032

Actual result

This will then return a GuzzleHttp\Exception\ClientException: Client error: 400 Bad request

Stack trace:

#0 /mysite/vendor/guzzlehttp/guzzle/src/Middleware.php(72): GuzzleHttp\Exception\RequestException::create()
#1 /mysite/vendor/guzzlehttp/promises/src/Promise.php(209): GuzzleHttp\Middleware::GuzzleHttp\{closure}()
#2 /mysite/vendor/guzzlehttp/promises/src/Promise.php(158): GuzzleHttp\Promise\Promise::callHandler()
#3 /mysite/vendor/guzzlehttp/promises/src/TaskQueue.php(52): GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
#4 /mysite/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(163): GuzzleHttp\Promise\TaskQueue->run()
#5 /mysite/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\Handler\CurlMultiHandler->tick()
#6 /mysite/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\Handler\CurlMultiHandler->execute()
#7 /mysite/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\Promise\Promise->invokeWaitFn()
#8 /mysite/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\Promise\Promise->waitIfPending()
#9 /mysite/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\Promise\Promise->invokeWaitList()
#10 /mysite/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\Promise\Promise->waitIfPending()
#11 /mysite/vendor/guzzlehttp/promises/src/Utils.php(121): GuzzleHttp\Promise\Promise->wait()
#12 /mysite/vendor/tweakwise/magento2-tweakwise/Model/Autocomplete/DataProvider/SuggestionDataProvider.php(149): GuzzleHttp\Promise\Utils::unwrap()
#13 /mysite/vendor/tweakwise/magento2-tweakwise/Model/Autocomplete/DataProvider.php(40): Tweakwise\Magento2Tweakwise\Model\Autocomplete\DataProvider\SuggestionDataProvider->getItems()
#14 /mysite/vendor/magento/module-search/Model/Autocomplete.php(35): Tweakwise\Magento2Tweakwise\Model\Autocomplete\DataProvider->getItems()
#15 /mysite/vendor/magento/module-search/Controller/Ajax/Suggest.php(45): Magento\Search\Model\Autocomplete->getItems()
#16 /mysite/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Search\Controller\Ajax\Suggest->execute()
#17 /mysite/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Search\Controller\Ajax\Suggest\Interceptor->callParent()
#18 /mysite/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Search\Controller\Ajax\Suggest\Interceptor->Magento\Framework\Interception\{closure}()
#19 /mysite/generated/code/Magento/Search/Controller/Ajax/Suggest/Interceptor.php(23): Magento\Search\Controller\Ajax\Suggest\Interceptor->_callPlugins()
#20 /mysite/vendor/magento/framework/App/Action/Action.php(111): Magento\Search\Controller\Ajax\Suggest\Interceptor->execute()
#21 /mysite/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\Action\Action->dispatch()
#22 /mysite/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Search\Controller\Ajax\Suggest\Interceptor->callParent()
#23 /mysite/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Search\Controller\Ajax\Suggest\Interceptor->Magento\Framework\Interception\{closure}()
#24 /mysite/generated/code/Magento/Search/Controller/Ajax/Suggest/Interceptor.php(32): Magento\Search\Controller\Ajax\Suggest\Interceptor->callPlugins()
#25 /mysite/vendor/magento/framework/App/FrontController.php(245): Magento\Search\Controller\Ajax\Suggest\Interceptor->dispatch()
#26 /mysite/vendor/magento/framework/App/FrontController.php(212): Magento\Framework\App\FrontController->getActionResponse()
#27 /mysite/vendor/magento/framework/App/FrontController.php(146): Magento\Framework\App\FrontController->processRequest()
#28 /mysite/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\FrontController->dispatch()
#29 /mysite/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\App\FrontController\Interceptor->callParent()
#30 /mysite/vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php(112): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#31 /mysite/vendor/magento/framework/Interception/Interceptor.php(135): Magento\Store\App\FrontController\Plugin\RequestPreprocessor->aroundDispatch()
#32 /mysite/vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(71): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#33 /mysite/vendor/magento/framework/Interception/Interceptor.php(135): Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch()
#34 /mysite/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#35 /mysite/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\Framework\App\FrontController\Interceptor->_callPlugins()
#36 /mysite/vendor/magento/framework/App/Http.php(116): Magento\Framework\App\FrontController\Interceptor->dispatch()
#37 /mysite/vendor/magento/framework/App/Bootstrap.php(264): Magento\Framework\App\Http->launch()
#38 /mysite/pub/index.php(147): Magento\Framework\App\Bootstrap->run()
#39 {main}

Expected result

The request will not happen and the calling function will return an empty result.

Filter param or page param still present in url

URL is not updated properly after visiting 1 page or removing the filter when ajax is anabled

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production

Pre-Conditions
Ajax filtering should be enabled
URL Strategy: QUERY PARAMS

Steps to reproduce for page param

  1. visit the category page where the page is applied. e.g. https://example.com/example_category_path?page=3
  2. click on Page 1 in the pagination toolbar

Actual result
URL address still has value: https://example.com/example_category_path?page=3

Expected result
https://example.com/example_category_path

Steps to reproduce for filter param

  1. visit the category page where the filter is applied. e.g. https://example.com/example_category_path?filter_name=filter_value
  2. Click on the "uncheck" mark on the active filter

Actual result
URL address still has value: https://example.com/example_category_path?filter_name=filter_value
Expected result
https://example.com/example_category_path

Deprecated Functionality: ctype_space()

Error creating new attribute:

Php 8.1
Magento 2.4.5-p1

  1. Deprecated Functionality: ctype_space(): Argument of type Magento\Framework\Phrase will be interpreted as string in the future in Tweakwise/Observer/RemoveTweakwiseSlugsBeforeSaveAttribute.php on line 42

  2. Tweakwise/Model/Catalog/Layer/Url/Strategy/FilterSlugManager.php on line 113

Spam of error messages in system.log when visiting a PDP of an out-of-stock product

Issue Brief

System.log gets spammed full of errors when landing on a PDP of which a product is not present in the Tweakwise Navigator. This issue is linked with the Tweakwise Export module. In our Magento environment, we disabled the "Export out of stock combined product children" in the Tweakwise Export module, but we keep the PDP of the out-of-stock visible on the store. This results in a spam of errors in system.xml with: Tweakwise GET resulted in a 404.

Environment

  • PHP Version: (7.4)
  • Magento Version: 2.4.3
  • Tweakwise Version: v5.2.1
  • Tweakwise Export Version: v5.1.1
  • Magento Deploy Mode: production, development and default

Steps to reproduce

  1. Have a functioning Magento environment with Tweakwise and Tweakwise export
  2. Have it connected to a Tweakwise Navigator
  3. Have a collection of products in your Magento environment
  4. Navigate to the adminhtml in Magento
  5. Stores -> Config -> Catalog -> Tweakwise -> Exclude Child Attributes -> "No"
  6. Save Config
  7. Go to Catalog and put a product out of stock, but keep the product enabled
  8. Export Products to Tweakwise
  9. Make sure the product indeed does not exist in the Tweakwise Navigator
  10. Visit the PDP of the product in the frontend of the Magento environment
  11. Check your logs for the error

Actual result

  1. [2023-10-19 13:40:03] main.ERROR: Client error: GET https://gateway.tweakwisenavigator.net/recommendations/grouped/x/x/cross-sell resulted in a 404 Not Found` response:

Expected result

  1. Not getting a spam of errors from disabling a valid option in Tweakwise Export

Additional comments

Disabling the product is in our case not an option, so this is a problem that needs to be solved programatically.

Limit max number of products for group codes applied when not needed

When the widget is inserted via the Admin area to the CMS page, the rule_id will be saved as a string, for example: "18"
image

After that, the module during the request to Tweekwise will use the setting: "Limit max number of products for group codes" as there is a check if !is_int("18"), however these setting(limit) suppose to be used for group codes, but not to the templates.

Environment

  • PHP Version: (8.1)
  • Magento Version: 2.4.5-p7
  • Tweakwise Version: 5.4.5
  • Magento Deploy Mode: production|development

Steps to reproduce

  1. Add the Tweawise widget to the home page
  2. Configure the widget that will show more than 5 products for instance
  3. Set the value to Admin > Stores > Configuration > Catalog > Tweakwise > Recommendations to setting "Limit max number of products for group codes" to 3
  4. Clean the cache and refresh the home page

Actual result

The number of products will be max 3

Expected result

The number of products won't be limited by 3

Possible fix:
image
OR:
image

Inconsistent product amount on catalog pages

Issue Brief

The amount of products displayed on catalog pages is not consistent per page. Product with 0 stock are being exported to Tweakwise and displayed in the feed on the Tweakwise side. In the Magento catalog however, the products aren't displayed. Out of stock products should not be exported to Tweakwise at all.

The 'Export out of stock combined product children' option is already set to 'No'.

Wrong crosssell and upsell templates of second Tweakwise environment

Issue Brief
We have a Magento 2 environment that has multiple store views. Each store view uses their own Tweakwise Environment and therefor a different Catalog / Tweakwise / General / Authentication Key in the admin.

When we go to the second store view level in the admin, then we see the different authentication key. But when we scroll down to Catalog / Tweakwise / Default Crosssell template and Default Upsell template then we see the templates of our other storeview.

So we believe the wrong authentication key is used when loading these templates. Possibly the one that's set on the default level.

Environment
PHP Version: (5.6|7.0|7.1)
Magento Version: 2.4.2-p2
Tweakwise Version: v3.3.8
Tweakwise Export Version: v2.1.0
Magento Deploy Mode: production
Steps to reproduce
Have two store views in Magento 2 en two environments in Tweakwise.
Set default and first store view Tweakwise authentication key to key number 1
Set second store view Tweakwise authentication to key number 2
Attempt to select crosssell and upsell templates under Catalog / Tweakwise / Default Crosssell template and Default Upsell template
Actual result
On default and both store view levels the selects show only the templates of environment number 1.

Expected result
On the default and first store view level, we expect to see templates of environment number 1.
On the second store view level, we expect to see templates of environment number 2.

This was previously reported here https://github.com/EmicoEcommerce/Magento2Tweakwise/issues/258 but after updating it does not appear to be fixed.

Wrong url for parent category in category tree

Issue Brief

Hello, we are using a category filter as a tree.
When a child category is selected, we encounter an issue with the parent category URL.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p3
  • Tweakwise Version: 5.5.1
  • Tweakwise Export Version: 5.3.1
  • Magento Deploy Mode: production

Steps to reproduce

  1. Install Magento from master branch.
  2. Export configurable product with multiple children and 2 configurable attributes to Tweakwise.
  3. Configure category filter in Tweakwise "Display filter values" as "Tree"
  4. Check the Magento child category overview page ( category level 3 or deeper)

Actual result

  1. Parent category URL has the same URL as Child. Can't follow the parent category.

Expected result

  • Parent category has the correct URL

At checkout page , Cross Sell Items calculated by last added Item.

Magento Uses LastAddedProductId data to get cross sell items for last added Items
This data should be removed from session after reading, to collect cross sell items for all cart Items at checkout Page.

This plugin
\Tweakwise\Magento2Tweakwise\Block\Checkout\Cart\Crosssell\Plugin::aroundGetLastAddedProduct
blocks unset operation on session.
And this this data will be used at checkout to collect cross sell Items.
\Magento\TargetRule\Block\Checkout\Cart\Crosssell::getItemCollection

and this function should return empty result to collect cross sell items for whole cart
$productsByLastAdded = $this->_getProductsByLastAddedProduct();

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p3
  • Tweakwise Version: latest
  • Tweakwise Export Version: latest
  • Magento Deploy Mode: production
  • Disable Tweakwise cross sell recommendation.

Steps to reproduce

  1. Create Crosssell rule for Product A
  2. Create Crosssell rule for Product B
  3. Add Item A to Cart.
  4. Add Item B to Cart

Actual result

  1. At checkout page , Items only for Product B will be listed as Cross Sell Items.

Expected result

  1. At checkout page ,Cross Items should be calculated by whole cart.

Ajax filtering breaks when caching is enabled.

The hash check where the salt is stored in the session, breaks when all Magento caches are enabled.

Shown error:

Incorrect/modified form parameters

Since caches are mandatory, how to make use of AJAX-filtering in v5.4.x ?

Used versions:

    "magento/product-community-edition": "2.4.5-p2", v2.4.5-p2
    "tweakwise/magento2-attributelanding-tweakwise": "^4.2", v4.2.0
    "tweakwise/magento2-tweakwise": "^5.4", v5.4.1
    "tweakwise/magento2-tweakwise-export": "^5.3", v5.3.1

app/etc/env.php

    'session' => [
        'save' => 'redis',
        'redis' => [
            'host' => '10.0.20.201',
            'port' => '6379',
            'password' => '',
            'timeout' => '2.5',
            'persistent_identifier' => '',
            'database' => '1',
            'compression_threshold' => '2048',
            'compression_library' => 'gzip',
            'log_level' => '1',
            'max_concurrency' => '6',
            'break_after_frontend' => '5',
            'break_after_adminhtml' => '30',
            'first_lifetime' => '600',
            'bot_first_lifetime' => '60',
            'bot_lifetime' => '7200',
            'disable_locking' => '0',
            'min_lifetime' => '60',
            'max_lifetime' => '2592000',
            'sentinel_master' => '',
            'sentinel_servers' => '',
            'sentinel_connect_retries' => '5',
            'sentinel_verify_master' => '0'
        ]
    ],
    'cache' => [
        'frontend' => [
            'default' => [
                'backend' => 'Cm_Cache_Backend_Redis',
                'backend_options' => [
                    'server' => '10.0.20.201',
                    'database' => '2',
                    'port' => '6379',
                    'password' => ''
                ]
            ],
            'page_cache' => [
                'backend' => 'Cm_Cache_Backend_Redis',
                'backend_options' => [
                    'server' => '10.0.20.201',
                    'database' => '3',
                    'port' => '6379',
                    'compress_data' => '0',
                    'password' => ''
                ]
            ]
        ]
    ],
    'cache_types' => [
        'config' => 1,
        'layout' => 1,
        'block_html' => 1,
        'collections' => 1,
        'reflection' => 1,
        'db_ddl' => 1,
        'compiled_config' => 1,
        'eav' => 1,
        'customer_notification' => 1,
        'config_integration' => 1,
        'config_integration_api' => 1,
        'full_page' => 1,
        'config_webservice' => 1,
        'translate' => 1,
        'vertex' => 1
    ],

PHP 8.2 Compatible

During bin/magento setup:upgrade, I get the error below:

Deprecated Functionality: Creation of dynamic property Tweakwise\Magento2Tweakwise\Setup\Patch\Data\AddRecommendationCategoryFieldsPatch::$eavSetupFactory is deprecated

I added patch below to fix the error

@package tweakwise/magento2-tweakwise

diff --git a/Setup/Patch/Data/AddRecommendationCategoryFieldsPatch.php b/Setup/Patch/Data/AddRecommendationCategoryFieldsPatch.php
--- a/Setup/Patch/Data/AddRecommendationCategoryFieldsPatch.php
+++ b/Setup/Patch/Data/AddRecommendationCategoryFieldsPatch.php	(date 1681985847341)
@@ -21,6 +21,11 @@
 	 */
 	private ModuleDataSetupInterface $moduleDataSetup;
 
+    /**
+     * @var EavSetupFactory
+     */
+	private EavSetupFactory $eavSetupFactory;
+
 	/**
 	 * @param ModuleDataSetupInterface $moduleDataSetup
 	 */

Call to a member function getProductIds() on null

Issue Brief

What is the purpose of this issue? Explain the background context.

Resolve the error:
Call to a member function getProductIds() on null

Which i sometimes see in the error logs.

Environment

  • PHP Version: (8.1)
  • Magento Version: 2.4.6-p6
  • Tweakwise Version: 6.0.1
  • Tweakwise Export Version: 7.0.1
  • Magento Deploy Mode: production
  • Third party modules:
    Trace does not have any third party modules visible.

Steps to reproduce

  1. \Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\NavigationContext:getResponse returns null which is possible when looking at the function return type.
  2. \Tweakwise\Magento2Tweakwise\Model\Catalog\Product\Collection::getProductIds
    $response = $this->navigationContext->getResponse(); return $response->getProductIds();
  3. this returns an empty array.

Actual result

  1. $response is null and than it cannot call getProductIds() on null.
  2. you get the following error:
  3. Error: Caught Error (500): Call to a member function getProductIds() on null

Expected result

  1. if $response is null than $response->getProductIds() will not be called.

Possible solution

diff --git a/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Product/Collection.php b/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Product/Collection.php
--- a/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Product/Collection.php
+++ b/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Product/Collection.php	(date 1719832609005)
@@ -148,6 +148,6 @@
     protected function getProductIds()
     {
         $response = $this->navigationContext->getResponse();
-        return $response->getProductIds();
+        return $response->getProductIds() ?? [];
     }
 }

Exception Undefined array key "start"

Issue Brief

Error on missing array key

Environment

  • PHP Version: (8.1)
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.0
  • Magento Deploy Mode: (development)

Steps to reproduce

  1. Install Magento from master branch.
  2. Open any page in store

Actual result

  1. Exception #0 (Exception): Warning: Undefined array key "start" in /var/www/html/vendor/tweakwise/magento2-tweakwise/Model/Client/Timer.php on line 54

Expected result
Not an exception

Show suggestions on search resuts page

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.2
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.8.3
  • Tweakwise Export Version:

The autocomplete show suggestions and categories, https://support.tweakwise.com/nl/cat/suggesties/62

We would like to show those too on the search results page, but can't seem to enable them there. Is there a method on how to achieve this?

Autocomplete:

image

Search results:

image

XSS on category overview page

XSS on the category overview page

XSS vulnerability could be used to steal customer data.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5, 2.4.6
  • Tweakwise Version: 5.6.5, 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production

details shared with support

Actual result

Script executed on page visit

Expected result

no script execution

Add support for Fastly (default for Adobe Commerce on Cloud projects)

Issue Brief

The latest version of the Tweakwise plugin does not support personal merchandising for Adobe commerce on Cloud projects, as these projects use Fastly instead of Varnish.

Environment

  • PHP Version: irrelevant
  • Magento Version: Adobe Commerce hosted on Cloud
  • Tweakwise Version: 7.x
  • Tweakwise Export Version: irrelevant
  • Magento Deploy Mode: irrelevant
  • Third party modules: irrelevant

Details

The \Tweakwise\Magento2Tweakwise\Helper\Cache::isVarnishEnabled method checks whether the value of the system/full_page_cache/caching_application configuration settings is equal to \Magento\PageCache\Model\Config::VARNISH (2).

However, even though Fastly is based on Varnish, it uses a different value: \Fastly\Cdn\Model\Config::FASTLY (42).

"Illegal offset type in isset or empty" TypeError after saving a Yes/No attribute

Issue Brief

Upon saving a Yes/No attribute, the following TypeError occurs:

TypeError: Illegal offset type in isset or empty in /app/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/FilterSlugManager.php:115

Environment

  • PHP Version: 8.2
  • Magento Version: 2.4.6-p6
  • Tweakwise Version: 7.0.1
  • Tweakwise Export Version: 7.0.1
  • Magento Deploy Mode: development
  • Third party modules:

Steps to reproduce

  1. Login to the Magento admin
  2. Go to "Stores" > "Attributes" > "Product"
  3. Click the "Add New Attribute" button.
  4. Enter some string in the "Default Label" input, choose "Yes/No" in the "Catalog Input Type for Store Owner" select, and click on "Save Attribute".

Screenshot 2024-07-10 at 15 01 55

Actual result

An error page is shown with the following contents:

TypeError: Illegal offset type in isset or empty in /app/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/FilterSlugManager.php:115
Stack trace:
#0 /app/vendor/tweakwise/magento2-tweakwise/Observer/CreateTweakwiseSlugsAfterSaveAttribute.php(31): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\FilterSlugManager->createFilterSlugByAttributeOptions(Array)
#1 /app/vendor/magento/framework/Event/Invoker/InvokerDefault.php(88): Tweakwise\Magento2Tweakwise\Observer\CreateTweakwiseSlugsAfterSaveAttribute->execute(Object(Magento\Framework\Event\Observer))
#2 /app/vendor/magento/framework/Event/Invoker/InvokerDefault.php(74): Magento\Framework\Event\Invoker\InvokerDefault->_callObserverMethod(Object(Tweakwise\Magento2Tweakwise\Observer\CreateTweakwiseSlugsAfterSaveAttribute), Object(Magento\Framework\Event\Observer))
#3 /app/vendor/magento/module-staging/Model/Event/Manager.php(97): Magento\Framework\Event\Invoker\InvokerDefault->dispatch(Array, Object(Magento\Framework\Event\Observer))
#4 /app/generated/code/Magento/Staging/Model/Event/Manager/Proxy.php(95): Magento\Staging\Model\Event\Manager->dispatch('catalog_entity_...', Array)
#5 /app/vendor/magento/framework/Model/AbstractModel.php(837): Magento\Staging\Model\Event\Manager\Proxy->dispatch('catalog_entity_...', Array)
#6 /app/vendor/magento/module-eav/Model/Entity/Attribute.php(356): Magento\Framework\Model\AbstractModel->afterSave()
#7 /app/vendor/magento/module-catalog/Model/ResourceModel/Eav/Attribute.php(256): Magento\Eav\Model\Entity\Attribute->afterSave()
#8 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Catalog\Model\ResourceModel\Eav\Attribute->afterSave()
#9 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->___callParent('afterSave', Array)
#10 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->Magento\Framework\Interception\{closure}()
#11 /app/generated/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute/Interceptor.php(32): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->___callPlugins('afterSave', Array, Array)
#12 /app/vendor/magento/framework/Model/ResourceModel/Db/AbstractDb.php(842): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->afterSave()
#13 /app/vendor/magento/framework/Model/ResourceModel/Db/AbstractDb.php(402): Magento\Framework\Model\ResourceModel\Db\AbstractDb->processAfterSaves(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#14 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\Model\ResourceModel\Db\AbstractDb->save(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#15 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Catalog\Model\ResourceModel\Attribute\Interceptor->___callParent('save', Array)
#16 /app/vendor/magento/module-catalog-search/Model/Attribute/SearchWeight.php(62): Magento\Catalog\Model\ResourceModel\Attribute\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#17 /app/vendor/magento/framework/Interception/Interceptor.php(135): Magento\CatalogSearch\Model\Attribute\SearchWeight->aroundSave(Object(Magento\Catalog\Model\ResourceModel\Attribute\Interceptor), Object(Closure), Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#18 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Catalog\Model\ResourceModel\Attribute\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#19 /app/generated/code/Magento/Catalog/Model/ResourceModel/Attribute/Interceptor.php(32): Magento\Catalog\Model\ResourceModel\Attribute\Interceptor->___callPlugins('save', Array, Array)
#20 /app/vendor/magento/framework/Model/AbstractModel.php(663): Magento\Catalog\Model\ResourceModel\Attribute\Interceptor->save(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#21 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\Model\AbstractModel->save()
#22 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->___callParent('save', Array)
#23 /app/vendor/mirasvit/module-navigation/src/LayeredNavigation/Plugin/Backend/SaveAttributeConfigPlugin.php(150): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->Magento\Framework\Interception\{closure}()
#24 /app/vendor/magento/framework/Interception/Interceptor.php(135): Mirasvit\LayeredNavigation\Plugin\Backend\SaveAttributeConfigPlugin->aroundSave(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor), Object(Closure))
#25 /app/vendor/mirasvit/module-seo-filter/src/SeoFilter/Plugin/Backend/SaveRewriteOnAttributeSavePlugin.php(133): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->Magento\Framework\Interception\{closure}()
#26 /app/vendor/magento/framework/Interception/Interceptor.php(135): Mirasvit\SeoFilter\Plugin\Backend\SaveRewriteOnAttributeSavePlugin->aroundSave(Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor), Object(Closure))
#27 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->Magento\Framework\Interception\{closure}()
#28 /app/generated/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute/Interceptor.php(50): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->___callPlugins('save', Array, NULL)
#29 /app/vendor/magento/module-catalog/Controller/Adminhtml/Product/Attribute/Save.php(303): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->save()
#30 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save->execute()
#31 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->___callParent('execute', Array)
#32 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->Magento\Framework\Interception\{closure}()
#33 /app/generated/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/Interceptor.php(23): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->___callPlugins('execute', Array, Array)
#34 /app/vendor/magento/framework/App/Action/Action.php(111): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->execute()
#35 /app/vendor/magento/module-backend/App/AbstractAction.php(151): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http))
#36 /app/vendor/magento/module-catalog/Controller/Adminhtml/Product/Attribute.php(78): Magento\Backend\App\AbstractAction->dispatch(Object(Magento\Framework\App\Request\Http))
#37 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Catalog\Controller\Adminhtml\Product\Attribute->dispatch(Object(Magento\Framework\App\Request\Http))
#38 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->___callParent('dispatch', Array)
#39 /app/vendor/magento/module-backend/App/Action/Plugin/Authentication.php(145): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#40 /app/vendor/magento/framework/Interception/Interceptor.php(135): Magento\Backend\App\Action\Plugin\Authentication->aroundDispatch(Object(Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#41 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#42 /app/generated/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/Interceptor.php(32): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->___callPlugins('dispatch', Array, Array)
#43 /app/vendor/magento/framework/App/FrontController.php(245): Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#44 /app/vendor/magento/framework/App/FrontController.php(212): Magento\Framework\App\FrontController->getActionResponse(Object(Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor), Object(Magento\Framework\App\Request\Http))
#45 /app/vendor/magento/framework/App/FrontController.php(146): Magento\Framework\App\FrontController->processRequest(Object(Magento\Framework\App\Request\Http), Object(Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\Interceptor))
#46 /app/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))
#47 /app/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\App\FrontController\Interceptor->___callParent('dispatch', Array)
#48 /app/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#49 /app/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\Framework\App\FrontController\Interceptor->___callPlugins('dispatch', Array, Array)
#50 /app/vendor/magento/framework/App/Http.php(116): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#51 /app/vendor/magento/framework/App/Bootstrap.php(264): Magento\Framework\App\Http->launch()
#52 /app/pub/index.php(30): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http\Interceptor))
#53 {main}
Fatal error: Uncaught Exception: User Error: Some transactions have not been committed or rolled back in /app/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php on line 4075 in /app/vendor/magento/framework/App/ErrorHandler.php:62
Stack trace:
#0 [internal function]: Magento\Framework\App\ErrorHandler->handler(256, 'Some transactio...', '/app/vendor/mag...', 4075)
#1 /app/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(4075): trigger_error('Some transactio...', 256)
#2 [internal function]: Magento\Framework\DB\Adapter\Pdo\Mysql->__destruct()
#3 {main}
  thrown in /app/vendor/magento/framework/App/ErrorHandler.php on line 62

Expected result

The Product attributes grid showing a "You saved the product attribute." notification.

Unable to use search when Tweakwise is down

Issue Brief

I would like to bring to your attention an issue that occurred last week regarding Tweakwise. During this time, we experienced a brief period of downtime which resulted in several features within our client's online store becoming non-functional. This was primarily due to failed API requests. As a result, visitors to the store were met with a less than ideal experience. Specifically, any attempts to search resulted in a 500 error.

Therefore, we would like to propose an alternative solution. We would appreciate the ability to either disable the Tweakwise system temporarily or automatically fallback to using the default Magento functionality.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6-p3 (Open Source)
  • Tweakwise Version: 5.1.1
  • Tweakwise Export Version: 5.0.3
  • Magento Deploy Mode: production

Steps to reproduce

  1. Tweakwise is down.
  2. Use the catalogsearch.
  3. It throws a 500 error.

Expected result

  • The catalogsearch should stil work when Tweakwise is down

Issues with filtered PLP and history

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.3
  • Magento Version: 2.4.7
  • Tweakwise Version: 6.0.1
  • Tweakwise Export Version:
  • Magento Deploy Mode: production
  • Third party modules: tweakwise/magento2-attributelanding-tweakwise

Steps to reproduce

  1. Create a landing page for multiple brands.
  2. filter a PLP on 2 or more of these brands
  3. deselect a brand

Actual result

It's not possible to deselect a brand.

Expected result

The brand is deselected and the results reloaded.

Workaround

For now, I've fixed this with this change:

--- a/vendor/tweakwise/magento2-tweakwise/view/frontend/web/js/navigation-form.js
+++ b/vendor/tweakwise/magento2-tweakwise/view/frontend/web/js/navigation-form.js
@@ -25,7 +25,7 @@
             noteMessageSelector: '.message.note',
             noticeMessageSelector: '.message.notice',
             isLoading: false,
-            ajaxCache: true,
+            ajaxCache: false,
         },

         currentXhr: null,

brand pages

There are many customers that are using brand pages.
There is an option to do it via landing pages but it's time-consuming and there is no automation for it.
Will be nice to have such an option out of the box with tweakwise extension.

Add setting to enable/disable showing parent category in layered navigation.

Issue Brief

Feature request:
In \Tweakwise\Magento2Tweakwise\Model\Client\Request::addCategoryFilter it is expected that everyone wants to show all the parent categories, but not all clients would want that. By adding a setting like "show_parent_category_filter" and if disabled don't add the parent categories in the category filter.

    public function addCategoryFilter($category)
    {
        $ids = [];
        if (is_numeric($category)) {
            $ids[] = $category;
            return $this->addCategoryPathFilter($ids);
        }
        /** @var Category $category */
        $parentIsRoot = in_array(
            (int) $category->getParentId(),
            [
                0,
                1,
                (int) $category->getStore()->getRootCategoryId()
            ],
            true
        );
        if (!$parentIsRoot) {
            // Parent category is added so that category menu is retained on the deepest category level
            $ids[] = (int) $category->getParentId();
        }
        $ids[] = (int) $category->getId();

        return $this->addCategoryPathFilter($ids);
    }

This function could change to something like this:

    public function addCategoryFilter($category)
    {
        $ids = [];
        if (is_numeric($category)) {
            $ids[] = $category;
            return $this->addCategoryPathFilter($ids);
        }
        /** @var Category $category */
        $parentIsRoot = in_array(
            (int) $category->getParentId(),
            [
                0,
                1,
                (int) $category->getStore()->getRootCategoryId()
            ],
            true
        );
        if (!$parentIsRoot && $this->scopeConfig->getValue(self::XML_SHOW_PARENT_CATEGORY_IN_FILTER)) {
            // Parent category is added so that category menu is retained on the deepest category level
            $ids[] = (int) $category->getParentId();
        }
        $ids[] = (int) $category->getId();

        return $this->addCategoryPathFilter($ids);
    }

This way clients can decide themselves if they want to show the parent categories.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5
  • Tweakwise Version: 5.7.5
  • Tweakwise Export Version: 6.0.0
  • Magento Deploy Mode: (production|development)
  • Third party modules:

Steps to reproduce

  1. Install Magento from master branch.
  2. [Example] Export configurable product with multiple children and 2 configurable attributes to Tweakwise.
  3. [Example] Configure color filter in Tweakwise
  4. ...

Actual result

  1. [Example] Error message appears: "Cannot save quote".
  2. [Screenshot, logs]
  3. ...

Expected result

List the expected results as a bullet list of expectations

  • [Example] Configurable product added to the shopping cart.
  • ...

When searching for a query longer then 100 chars, Tweakwise gives a 400 error back.

Go to a site utilizing Tweakwise Search and place the following in the query string:
https://.../catalogsearch/result/index?q=Lorem+ipsum+dolor+sit+amet%2C+consectetur+adipiscing+elit.+Phasellus+pretium+magna+ac+metus+placerat+pdasdasdasdas

An error report page comes up and something like the following is written to the system log:

Next Tweakwise\Magento2Tweakwise\Exception\ApiException: Client error: `GET https://gateway.tweakwisenavigator.net/navigation-search/` resulted in a `400 Bad Request` response:
<?xml version="1.0"?>
<error xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-i (truncated...)
 in /vendor/tweakwise/magento2-tweakwise/Model/Client.php:163
Stack trace:
#0 /vendor/guzzlehttp/promises/src/Promise.php(209): Tweakwise\Magento2Tweakwise\Model\Client->Tweakwise\Magento2Tweakwise\Model\{closure}()
#1 /vendor/guzzlehttp/promises/src/Promise.php(158): GuzzleHttp\Promise\Promise::callHandler()
#2 /vendor/guzzlehttp/promises/src/TaskQueue.php(52): GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
#3 /vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(163): GuzzleHttp\Promise\TaskQueue->run()
#4 /vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\Handler\CurlMultiHandler->tick()
#5 /vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\Handler\CurlMultiHandler->execute()
#6 /vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\Promise\Promise->invokeWaitFn()
#7 /vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\Promise\Promise->waitIfPending()
#8 /vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\Promise\Promise->invokeWaitList()
#9 /vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\Promise\Promise->waitIfPending()
#10 /vendor/tweakwise/magento2-tweakwise/Model/Client.php(171): GuzzleHttp\Promise\Promise->wait()
#11 /vendor/tweakwise/magento2-tweakwise/Model/Client.php(286): Tweakwise\Magento2Tweakwise\Model\Client->doRequest()
#12 /vendor/magento/framework/Interception/Interceptor.php(58): Tweakwise\Magento2Tweakwise\Model\Client->request()
#13 /vendor/magento/framework/Interception/Interceptor.php(138): Tweakwise\Magento2Tweakwise\Model\Client\Interceptor->___callParent()
#14 /vendor/magento/framework/Interception/Interceptor.php(153): Tweakwise\Magento2Tweakwise\Model\Client\Interceptor->Magento\Framework\Interception\{closure}()
#15 /generated/code/Tweakwise/Magento2Tweakwise/Model/Client/Interceptor.php(23): Tweakwise\Magento2Tweakwise\Model\Client\Interceptor->___callPlugins()
#16 /vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/NavigationContext.php(156): Tweakwise\Magento2Tweakwise\Model\Client\Interceptor->request()
#17 /vendor/tweakwise/magento2-tweakwise/Model/Observer/CatalogSearchRedirect.php(63): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\NavigationContext->getResponse()
#18 /vendor/magento/framework/Event/Invoker/InvokerDefault.php(88): Tweakwise\Magento2Tweakwise\Model\Observer\CatalogSearchRedirect->execute()
#19 /vendor/magento/framework/Event/Invoker/InvokerDefault.php(74): Magento\Framework\Event\Invoker\InvokerDefault->_callObserverMethod()
#20 /vendor/magento/module-staging/Model/Event/Manager.php(97): Magento\Framework\Event\Invoker\InvokerDefault->dispatch()
#21 /generated/code/Magento/Staging/Model/Event/Manager/Proxy.php(95): Magento\Staging\Model\Event\Manager->dispatch()
#22 /vendor/magento/framework/App/FrontController.php(279): Magento\Staging\Model\Event\Manager\Proxy->dispatch()
#23 /vendor/magento/framework/App/FrontController.php(211): Magento\Framework\App\FrontController->dispatchPreDispatchEvents()
#24 /vendor/magento/framework/App/FrontController.php(147): Magento\Framework\App\FrontController->processRequest()
#25 /vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\FrontController->dispatch()
#26 /vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\App\FrontController\Interceptor->___callParent()
#27 /vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#30 /vendor/fastly/magento2/Model/FrontControllerPlugin.php(135): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#31 /vendor/magento/framework/Interception/Interceptor.php(135): Fastly\Cdn\Model\FrontControllerPlugin->aroundDispatch()
#32 /vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(71): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#33 /vendor/magento/framework/Interception/Interceptor.php(135): Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch()
#34 /vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}()
#35 /generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\Framework\App\FrontController\Interceptor->___callPlugins()
#36 /vendor/magento/framework/App/Http.php(116): Magento\Framework\App\FrontController\Interceptor->dispatch()
#37 /vendor/magento/framework/App/Bootstrap.php(264): Magento\Framework\App\Http->launch()
#38 /pub/index.php(30): Magento\Framework\App\Bootstrap->run()

I've looked into setting the max query length to 100. And this does resolve direct input through the input field and "fixes" the issue: catalog/search/max_query_length

However direct input through the url still causes an error to be thrown. This could be fixed by also applying this max_query_length to the Tweakwise module:

Index: vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/QueryParameterStrategy.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/QueryParameterStrategy.php b/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/QueryParameterStrategy.php
--- a/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/QueryParameterStrategy.php	
+++ b/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/QueryParameterStrategy.php	(date 1695988662625)
@@ -21,6 +21,7 @@
 use Magento\Catalog\Model\Category;
 use Magento\Framework\App\Request\Http as MagentoHttpRequest;
 use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Search\Helper\Data;
 
 class QueryParameterStrategy implements UrlInterface, FilterApplierInterface, CategoryUrlInterface
 {
@@ -93,7 +94,8 @@
         UrlModel $url,
         StrategyHelper $strategyHelper,
         CookieManagerInterface $cookieManager,
-        TweakwiseConfig $config
+        TweakwiseConfig $config,
+        private Data $searchConfig,
     ) {
         $this->url = $url;
         $this->strategyHelper = $strategyHelper;
@@ -450,7 +452,13 @@
      */
     protected function getSearch(MagentoHttpRequest $request)
     {
-        return $request->getQuery(self::PARAM_SEARCH);
+        $search = $request->getQuery(self::PARAM_SEARCH);
+
+        if ($maxQueryLength = $this->searchConfig->getMaxQueryLength()) {
+            $search = substr((string) $search, 0, $maxQueryLength);
+        }
+
+        return $search;
     }
 
     /**

This would mean that this is an configuration detail to be followed. An alternative option is to enforce this directly.

I'm also curious if this can't be resolved in Tweakwise. I understand a limit is used, but perhaps it could be handled more gracefully.

Broken swagger page

Swagger page doesn't show allowed resources because of improper type declaration in PHP docs.
Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: developer

Steps to reproduce

  1. Install Magento from master branch.
  2. visit swagger page. e.g. https://www.example.com/swagger

Actual result

  1. error shown "Failed to load API definition"

Expected result

allowed API endpoints are shown.

Bugs in search.banner.phtml

#90 {main} {"exception":"[object] (Exception(code: 0): Warning: Undefined array key 0 in /var/domains/xxx/application/releases/251/var/view_preprocessed/pub/static/vendor/tweakwise/magento2-tweakwise/view/frontend/templates/searchbanner/search.banner.phtml on line 1 at /var/domains/xxx/application/releases/251/vendor/magento/framework/App/ErrorHandler.php:62)"} []
[2023-08-16T06:30:49.723462+00:00] main.CRITICAL: Exception: Warning: Undefined array key 0 in /var/domains/xxx/application/releases/251/var/view_preprocessed/pub/static/vendor/tweakwise/magento2-tweakwise/view/frontend/templates/searchbanner/search.banner.phtml on line 1 in /var/domains/xxx/application/releases/251/vendor/magento/framework/App/ErrorHandler.php:62

Error on search page because there is no check in file vendor/tweakwise/magento2-tweakwise/view/frontend/templates/searchbanner/search.banner.phtml on line 4 if $banners[0] key exists.

Also on line 9 attribute 'type' on anchor has syntax error:
is:
<a href="<?=$banner['clickurl'];?>" type=<?=$banner['clicktarget'];?>">
should be:
<a href="<?=$banner['clickurl'];?>" type="<?=$banner['clicktarget'];?>">

Empty product collections returned by ElasticSearch - Wrong collectionProvider used, when fixed, filtering and toolbar not working

Issue Brief

Sometimes, no product collection is being returned by Tweakwise. Because of a republish or the API that is temporarily unavailable.
If this happens, the logic should fallback on the default Magento collection, but it returns an empty collection because of an error in ElasticSearch. Biggest problem is that empty product pages are cached too, so unless you flush the cache, customers will see an empty product page.

Prerequisties:

  • Simulate no collection returned by Tweakwise (which sometimes happens), by adding the following to at the beginning of the function:
    vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/ItemCollectionProvider.php :: getCollection
$this->config->setTweakwiseExceptionThrown(); //Mandatory, but makes sure the default Magento filtering is being used
return $this->originalProvider->getCollection($category);
  • Go to a Product List Page, containing Layered Navigation

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p2 Commerce
  • Tweakwise Version: 5.3.3
  • Tweakwise Export Version: 5.2.0
  • ElasticSearch7
  • Magento Deploy Mode: production, development, doesn't matter
  • Third party modules:

Expected result:

  • Working product collection, filters, toolbar

Actual result:

  • Empty product collection, no product list. Biggest problem is that empty product pages are cached too, so unless you flush the cache, customers will see an empty product page.

Little information I accuired from the investigation:
If the current logic fallback, calls the collection of the originalProvider, it still retrieves the products from ElasticSearch.
However, an error occurs fetching the products from ElasticSearch because the sortOrder is empty (no field and no direction are given).

{"error":{"root_cause":[{"type":"x_content_parse_exception","reason":"[1:107] [field_sort] failed to parse field [order]"}],"type":"x_content_parse_exception","reason":"[1:107] [field_sort] failed to parse field [order]","caused_by":{"type":"illegal_argument_exception","reason":"No enum constant org.elasticsearch.search.sort.SortOrder."}},"status":400}

This sortOrder is normally given by a collectionProvider of ElasticSearch (a virtualType: elasticsearchLayerCategoryItemCollectionProvider)
But Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider is used by Tweakwise instead.

If you change the following part to ElasticSearch, you do get a collection (which also is the correct collection), but filtering and the toolbar are broken (meaning: collection is not filtered, nor pagination does work).

    <!-- Tweakwise virtual collection provider types used in overrides of magento collection providers -->
    <virtualType name="Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\ItemCollectionProvider\Category" type="Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\ItemCollectionProvider">
        <arguments>
            <argument name="originalProvider" xsi:type="object">elasticsearchLayerCategoryItemCollectionProvider</argument>
            <argument name="navigationContext" xsi:type="object">Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\NavigationContext\Category</argument>
        </arguments>
    </virtualType>

Filters no longer combining after update to v5.4.4

Issue Brief

Hello, we recently updated the Tweakwise extension in our store to the latest version, however, while doing this we noticed an issue that's causing filters to no longer be combined

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p2
  • Tweakwise Version: 5.4.4 (and higher)
  • Magento Deploy Mode: production

Steps to reproduce

  1. Install affected version
  2. Visit page with navigation
  3. Enable one filter from one attribute
  4. Try to also select another filter from another attribute

Actual result

  1. Only the most recently selected filter is selected
  2. The url turns into (domain)//category/?filter=1

Expected result

List the expected results as a bullet list of expectations

  • The selected filter is added to the list of filters
  • URL becomes (domain)/category?filter=1&other_filter=2

Autocomplete search instant result

At the moment the autocomplete of the search is done via Magento logic. This causes delay in retrieving the result. The data you want to show in the autocomplete window is available in Tweakwise. It would be nice if there was an option where you can choose to retrieve the data directly from Tweakwise without the intervention of Magento. Since autocomplete results are not cached in Magento, this makes a significant difference when this comes directly from Tweakwise and can be handled completely via the frontend.

Question: how do you get featured products by id/name

How do you get featured products by id/name from the following list in Tweakwise in Magento 2?

tweakwise

It's not clear from the documentation, readme or in the code. You can choose a default featured "default featured product template", but it's not clear where the products are coming from.

Maybe a good idea to put this information in the documentation.

ajax filtering for category tree, attribute landing page

Ajax filtering doesn't work consistently with category filters, or attribute landing page filters.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production

Pre-conditions

  • ajax filtering is enabled
  • category tree is added to the filter template
  • there are managed landing pages

Steps to reproduce

  1. visit category page
  2. click on the link in the category tree or filter that matches the landing page

Actual result

page fully refreshed

Expected result

content updated with ajax

Error spams logs

Issue Brief

Error spams logs when visiting empty cart page

Exception: Warning: Undefined variable $products in vendor/tweakwise/magento2-tweakwise/Block/Checkout/Cart/Crosssell/Plugin.php on line 253

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.6
  • Tweakwise Version: 5.7.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production

Steps to reproduce

  1. enable cross-sell products on a cart page
  2. visit cart page. there should not be any product in a basket

Actual result

  1. error is written in the log file var/log/system.log
  2. in developer mode it shows 500 page

Expected result

  • no any errors.

Hidden filters doesn't work with AJax

Issue Brief

Tweakwise allows setting the filter as "hidden|not to show" in the filter template but filtering by it is still workable.
Such filters could be used for marketing to point users to special products.

Environment

PHP Version: 8.1
Magento Version: 2.4.6
Tweakwise Version: 5.7.3
Tweakwise Export Version: 5.4.0
Magento Deploy Mode: production

Pre-conditions

  • ajax filtering is enabled
  • configure filter template with hidden filter
  • URL strategy: Query params
    Screenshot from 2024-02-07 10-48-23

Steps to reproduce

  1. visit a category with an applied "hidden" filter. e.g. /category_path/?test_hidden=test
  2. try to navigate to the second page, or change sorting, or add one more filter

Actual result

  • hidden filter is not applied to the result

Expected result

  • the results should be the same: despite of whether it's rendered by AJax or not.

Filtering gives no results

When users use a filter with an ampersand (&) the site gives no results

For example - you can filter in the layered navigation. If you choose "Klauwlijm & blokjes" you get no results (while there are 23 items in the category), seems like it is because of the ampersand (&) in the chosen attribute.
image
image

If we replace the ampersand (&) in the URL with %26, it does work.

image

We faced with that after updating module Tweakwise from 5.3.3 to 5.6.5, after some time of investigation we figured out that in version 5.4.0 it still works but already in 5.4.1 it isn't. So we dive into the changes what was added in this version and understood that it stop working after apply changes from this PR that was part of changes in version 5.4.1
https://github.com/EmicoEcommerce/Magento2Tweakwise/pull/37/files

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5-p4
  • Tweakwise Version: 5.6.3
  • Tweakwise Export Version: 5.4.0
  • Magento Deploy Mode: production and development

Steps to reproduce

  1. Make sure that you have installed Tweakwise version higher than 5.4.0

Actual result

  1. Go to the Product Listing Page (PLP)
  2. In the Tweakwise Layered Navigation choose the attribute that contain ampersand (&) in the title and contains some products
  3. Look on the result (no products)

Expected result

  1. Go to the Product Listing Page (PLP)
  2. In the Tweakwise Layered Navigation choose the attribute that contain ampersand (&) in the title and contains some products
  3. Products shown as when filtering the options without ampersand (&) in the title.

Autocomplete fails with Guzzle 7.3

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.1
  • Magento Version: 2.4.5
  • Tweakwise Version: 5.6.3
  • Tweakwise Export Version: 5.4.0

The autocomplete gives this error:

system.log:[2023-08-29T08:40:49.190266+00:00] main.CRITICAL: Error: Call to undefined function GuzzleHttp\Promise\unwrap() in /home/svc-tsf_acc/releases/9987b0a7/vendor/tweakwise/magento2-tweakwise/Model/Autocomplete/DataProvider/SuggestionDataProvider.php:149

When using Guzzle 7.x (I'm using 7.3).

Proposed fix:

--- a/vendor/tweakwise/magento2-tweakwise/Model/Autocomplete/DataProvider/SuggestionDataProvider.php
+++ b/vendor/tweakwise/magento2-tweakwise/Model/Autocomplete/DataProvider/SuggestionDataProvider.php
@@ -12,7 +12,7 @@
 use Tweakwise\Magento2Tweakwise\Model\Client\Response\AutocompleteProductResponseInterface;
 use Tweakwise\Magento2Tweakwise\Model\Client\Response\Suggestions\SuggestionsResponse;
 use Tweakwise\Magento2Tweakwise\Model\Config;
-use function GuzzleHttp\Promise\unwrap;
+use GuzzleHttp\Promise\Utils;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Search\Model\Autocomplete\ItemInterface;

@@ -146,7 +146,7 @@
         }

         $results = [];
-        $responses = unwrap($promises);
+        $responses = Utils::unwrap($promises);
         foreach ($responses as $key => $response) {
             if ($response instanceof AutocompleteProductResponseInterface) {
                 $results[] = $this->dataProviderHelper->getProductItems($response);

AttributeSlug::getSlug() must return a string

Issue Brief

What is the purpose of this issue? Explain the background context.

Environment

  • PHP Version: 8.3
  • Magento Version: 2.4.7
  • Tweakwise Version: 6.0.1

Steps to reproduce

[2024-06-05T07:50:32.855005+00:00] main.CRITICAL: TypeError: Tweakwise\Magento2Tweakwise\Model\AttributeSlug::getSlug(): Return value must be of type string, null returned in /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/AttributeSlug.php:51
Stack trace:
#0 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/AttributeSlugRepository.php(89): Tweakwise\Magento2Tweakwise\Model\AttributeSlug->getSlug()
#1 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/FilterSlugManager.php(97): Tweakwise\Magento2Tweakwise\Model\AttributeSlugRepository->save()
#2 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/PathSlugStrategy.php(439): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\FilterSlugManager->getSlugForFilterItem()
#3 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/PathSlugStrategy.php(379): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\PathSlugStrategy->buildFilterSlugPath()
#4 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/PathSlugStrategy.php(167): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\PathSlugStrategy->buildFilterUrl()
#5 /home/svc-tsf_prod/releases/80de182d/vendor/magento/framework/Interception/Interceptor.php(58): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\PathSlugStrategy->getAttributeSelectUrl()
#6 /home/svc-tsf_prod/releases/80de182d/vendor/magento/framework/Interception/Interceptor.php(138): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\PathSlugStrategy\Interceptor->___callParent()
#7 /home/svc-tsf_prod/releases/80de182d/vendor/tweakwise/magento2-attributelanding-tweakwise/src/Plugin/PathSlugStrategyPlugin.php(74): Tweakwise\Magento2Tweakwise\Model\Catalog\Layer\Url\Strategy\PathSlugStrategy\Interceptor->Magento\Framework\Interception\{closure}()
#8 /home/svc-tsf_prod/releases/80de182d/vendor/magento/framework/Interception/Interceptor.php(135): Tweakwise\AttributeLandingTweakwise\Plugin\PathSlugStrategyPlugin->aroundGetAttributeSelectUrl()

Solution:

    public function getSlug(): string
    {
        return $this->getData(self::SLUG) ?? '';
    }

Crosssell recommendation will return empty list, if we dont use Tweakwise Crosssel Recommendation.

Magento's default Crosssell recommendation will return empty list, if we dont use Tweakwise Cross Recommendation.

Environment

  • PHP Version: (8.1)
  • Magento Version:2.4.5-p2
  • Tweakwise Version:v5.4.3
  • Tweakwise Export Version:v5.1.2
  • Magento Deploy Mode: (production)
  • Third party modules:

Steps to reproduce

-Define Crosssell rule via Magento Target Rule (be sure that your indexers are up to date)
-Empty your cart
-Add one item to your cart

Actual result
-Crosssell recommendation is empty.
-if you try to add another product, you can see recommendation, for previously added Items (excluding last Item.)

Expected result
-Matching Target Rule must execute and recommendations should be listed for last added Item.

Issue is happening because of this plugin.

\Tweakwise\Magento2Tweakwise\Block\Checkout\Cart\Crosssell\Plugin::aroundGetLastAddedProduct

public function aroundGetLastAddedProduct(Crosssell $crossell, Closure $proceed)
    {
        $product = null;
        $productId = $this->_getLastAddedProductId();
        if ($productId) {
            try {
                $product = $this->productRepository->getById($productId);
            } catch (NoSuchEntityException $e) {
                $product = null;
            }
        }
        $this->lastAddedProduct = $product;
    }

original magento implimantation is returning also last Added Item. But current tweakwise plugin is not returning this value.
and Magento's recommendation logic breaks.

\Magento\TargetRule\Block\Checkout\Cart\Crosssell::getItemCollection

Magento first tries to get recommendations based on last added Item, by this call.

 $productsByLastAdded = $this->_getProductsByLastAddedProduct(); 

but it fails because of \Tweakwise\Magento2Tweakwise\Block\Checkout\Cart\Crosssell\Plugin::aroundGetLastAddedProduct plugin.

 public function getItemCollection()
    {
        if ($this->_items === null) {
            // if has just added product to cart - load cross-sell products for it
            $productsByLastAdded = $this->_getProductsByLastAddedProduct();
            $limit = $this->getPositionLimit();
            if (!empty($this->_getCartProducts()) && count($productsByLastAdded) < $limit) {
                // reset collection
                $this->_linkCollection = null;
                parent::getItemCollection();
                // products by last added are preferable
                $this->_items = $productsByLastAdded + $this->_items;
                $this->_sliceItems();
            } else {
                $this->_items = $productsByLastAdded;
            }
            $this->_sliceItems();
        }
        return $this->_items;
    }

Subcategory url is wrong when customer filter the results on category page

Issue Brief

On category page we are showing subcategory slider on top (before products). The url of that subcategories are wrong.

Environment

  • PHP Version: (8.2)
  • Magento Version: 2.4.6-p3
  • Tweakwise Version: 5.3.3
  • Tweakwise Export Version:
  • Magento Deploy Mode: (development)
  • Third party modules:

Steps to reproduce

  1. Install Tweakwise module and do all required configuration and export data
  2. Create a category structure as like -
  • root category

    • tables
      • Dining tables
        • Oval
        • Round

    Product attributes -
    -Shape
    - Round
    - Oval

  1. Go to - base_url/tables-dinning-tables
  2. Filter by shape - round
  3. Now url will be - base_url/tables-dinning-tables/shape/round
  4. But subcategory url is wrong.

The issue is in code -
File - vendor/tweakwise/magento2-tweakwise/Model/Catalog/Layer/Url/Strategy/PathSlugStrategy.php
Line - 563

$url = implode('/', array_unique($explode));

array_unique is removing save values. There is comment that it is done to avoid double values. But in ur case we need to have that values.

Actual result

  1. Subcategory url - base_url/tables-dinning-tables/round/shape/

Expected result

Subcategory url should be - base_url/tables-dinning-tables/round/shape/round

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.