Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Unreleased
### Add
- Add cart button for product list
- Implement proxy - more information in README file

### Change
- Support tab navigation for search, suggest and paging components
Expand Down
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ For more advanced features please check our official [WebComponnents documentati
- [Test FTP Connection Button](#test-ftp-connection)
- [Update Field Roles Button](#update-field-roles)
- [Advanced Settings](#advanced-settings)
- [Proxy](#proxy)
- [Features Settings](#features-settings)
- [Using FACT-Finder® on category pages](#using-fact-finder-on-category-pages)
- [Feed Settings](#feed-settings)
Expand All @@ -39,6 +40,8 @@ For more advanced features please check our official [WebComponnents documentati
- [Click on Product](#click-on-product)
- [Add Product to Cart](#add-product-to-cart)
- [Place an Order](#place-an-order)
- [Modification Examples](#modifications-examples)
- [Enrich data received from FACT-Finder](#enrich-data-received-from-fact-finder)
- [Contribute](#contribute)
- [License](#license)

Expand Down Expand Up @@ -121,10 +124,44 @@ This functionality uses form data, so there is no need to save first.

### Advanced Settings
![Advanced Settings](docs/assets/advanced-settings.png "Advanced settings")
* `Anonymize User ID?` - check this option if you want to send user id with tracking requests in anonymized form. By default the regular id field from user table is sent.
* `Anonymize User ID?` - check this option if you want to send user id with tracking requests in anonymized form. By default, the regular id field from user table is sent.
* `Use Proxy` - check this option if you want each request sends by Web Components first reach the dedicated module controller which forwards it to the FACT-Finder.
**Note:** If you plan to use proxy, consider reading below paragraph as it requires full instruction how to enable it properly.
* `How to count single click on "Add to cart" button?` - select how would you like to count single click on "Add to cart" button
* `Send the SID as userId when user not logged in?`

#### Proxy
Proxy feature adds a oxid controller which serves as a middleware between Web Components and FACT-Finder®.
The data flow with proxy enabled is illustrated by the graph below.
![Communication Overview](docs/assets/communication-overview.png "Communication Overview")
Having a middleware controller brings many possibilities to customize the request and the response. You can use `EnrichProxyDataEvent` to enrich data received from FACT-Finder. You can find more
details about implementation [here](#enrich-data-received-from-fact-finder-in-proxycontroller).
In addition, if forwarded request does not result with a correct response, you can implement fallback strategy, starting from this point.

```php
//src/Controller/SearchResultController.php:84
protected function fallback(): void
{
//this function could be used to implement fallback logic in case of any communication error.
$this->showJsonAndExit('Error: Unable to process the request.');
}
```

To enable proxy you need to change your HTTP server configuration by adding rewrite rules.
This is necessary because Web Components appends a URL parts to the base URL making it unreadable by the Oxid.
This is because Oxid use query parameters `cl` and `fnc` to instantiate specific controller and execute its function.
There is no routing that use url parts, hence any AJAX requests must target index.php file with the aforementioned parameters.
Without these rules any request will lead to 404.

APACHE

```apache
RewriteRule ^rest/v5/(.*)$ index.php?cl=search_result&fnc=proxy&$1 [L]
```

**Note:** Sending each request to FACT-Finder instance trough Shopware, you lose on performance as each request need to be handled first by HTTP server and then, by Shopware itself. This additional traffic could be easily avoided by not activating this feature if there's no clear reason to use it.


### Features Settings
![Features Settings](docs/assets/features-settings.png "Features settings")

Expand Down Expand Up @@ -245,6 +282,35 @@ We offer a `registerAddToCartListener` function which helps to register `click`
### Place an Order
This event is tracked by the `ff-checkout-tracking` element which is implemented on order confirmation page

## Modifications Examples

### Enrich data received from FACT-Finder

```php

use Omikron\FactFinder\Oxid\Event\EnrichProxyDataEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class EnrichProxyDataEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [EnrichProxyDataEvent::class => 'enrichData'];
}

public function enrichData(EnrichProxyDataEvent $event): void
{
$data = $event->getData();
$data['example_data'] = [
'some_data' => 'data_1',
'next_data' => 'data_2',
];
$event->setData($data);
}
}

```

## Contribute
For more information, click [here](.github/CONTRIBUTING.md)

Expand Down
15 changes: 7 additions & 8 deletions metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,13 @@
'value' => true,
'position' => $settingPosition++,
],
// TODO Refactor proxy
// [
// 'group' => 'ffAdvanced',
// 'name' => 'ffUseProxy',
// 'type' => 'bool',
// 'value' => false,
// 'position' => $settingPosition++,
// ],
[
'group' => 'ffAdvanced',
'name' => 'ffUseProxy',
'type' => 'bool',
'value' => false,
'position' => $settingPosition++,
],
[
'group' => 'ffAdvanced',
'name' => 'ffSidAsUserId',
Expand Down
40 changes: 27 additions & 13 deletions src/Controller/SearchResultController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

use Omikron\FactFinder\Communication\Client\ClientBuilder;
use Omikron\FactFinder\Communication\Version;
use Omikron\FactFinder\Oxid\Event\EnrichProxyDataEvent;
use Omikron\FactFinder\Oxid\Subscriber\EnrichProxyDataEventSubscriber;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingServiceInterface;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

class SearchResultController extends FrontendController
{
Expand Down Expand Up @@ -40,20 +42,32 @@ public function proxy(): void

switch ($httpMethod) {
case 'GET':
$query = (string) $this->removeOxidParams(parse_url($currentUrl, PHP_URL_QUERY));
$this->showJsonAndExit($this->unwrapResponse($client->request('GET', $endpoint . '?' . $query)));
$query = (string) $this->removeOxidParams(parse_url($currentUrl, PHP_URL_QUERY));
$response = $client->request('GET', $endpoint . '?' . $query);

break;
case 'POST':
$this->showJsonAndExit($this->unwrapResponse($client->request('POST', $endpoint, [
'body' => $this->getRequest()->getContent(),
$rawBody = file_get_contents('php://input');
$body = json_decode($rawBody, true) ?: [];

if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('Invalid JSON in request body');
}

$response = $client->request('POST', $endpoint, [
'body' => json_encode($body),
'headers' => ['Content-Type' => 'application/json'],
])));
]);

break;
default:
throw new \Exception(sprintf('HTTP Method %s is not supported', $httpMethod));
}
$eventDispatcher = new EventDispatcher();
$eventDispatcher->addSubscriber(new EnrichProxyDataEventSubscriber());
$event = new EnrichProxyDataEvent(json_decode($response->getBody()->getContents(), true) ?? []);
$eventDispatcher->dispatch($event, EnrichProxyDataEvent::class);
$this->showJsonAndExit(json_encode($event->getData()));
} catch (\Exception $e) {
$this->fallback();
echo json_encode(['error' => $e->getMessage()]);
Expand All @@ -71,7 +85,7 @@ protected function showJsonAndExit(string $jsonResponse): void
protected function fallback(): void
{
// this function could be used to implement fallback logic in case of any communication error.
$this->showJsonAndExit('');
$this->showJsonAndExit('Error: Unable to process the request.');
}

protected function getConfigParam(string $key): string
Expand All @@ -86,16 +100,16 @@ protected function getConfigParam(string $key): string
private function getEndpoint(string $currentUrl): string
{
preg_match('#/([A-Za-z]+\.ff|rest/v[^?]*)#', $currentUrl, $match);

return $match[1] ?? '';
}

private function removeOxidParams(string $queryString): string
private function removeOxidParams(?string $queryString): string
{
return preg_replace('/(fnc|cl)=[A-Za-z0-9_]*&?/', '', $queryString);
}
if ($queryString === null) {
return '';
}

private function unwrapResponse(ResponseInterface $response): string
{
return $response->getBody()->getContents();
return preg_replace('/(fnc|cl)=[A-Za-z0-9_]*&?/', '', $queryString);
}
}
27 changes: 27 additions & 0 deletions src/Event/EnrichProxyDataEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Omikron\FactFinder\Oxid\Event;

use Symfony\Contracts\EventDispatcher\Event;

class EnrichProxyDataEvent extends Event
{
private array $data;

public function __construct(array $data)
{
$this->data = $data;
}

public function getData(): array
{
return $this->data;
}

public function setData(array $data): void
{
$this->data = $data;
}
}
13 changes: 4 additions & 9 deletions src/Model/Config/Communication.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,9 @@ protected function getLocale(string $abbr): string

protected function getServerUrl(): string
{
return (string) $this->moduleSettingService->getString('ffServerUrl', 'ffwebcomponents');

// TODO Refactor proxy
// return (string) $this->moduleSettingService->getBoolean('ffUseProxy', 'ffwebcomponents') ?
// 'index.php' :
// (string) $this->moduleSettingService->getString('ffServerUrl', 'ffwebcomponents');
return (string) $this->moduleSettingService->getBoolean('ffUseProxy', 'ffwebcomponents') ?
'' :
(string) $this->moduleSettingService->getString('ffServerUrl', 'ffwebcomponents');
}

protected function getCategoryPath(Category $category): string
Expand Down Expand Up @@ -118,8 +115,6 @@ protected function getApiVersion(): string

private function useProxy(): bool
{
return false;
// TODO Refactor proxy
// return (bool) $this->moduleSettingService->getBoolean('ffUseProxy', 'ffwebcomponents');
return (bool) $this->moduleSettingService->getBoolean('ffUseProxy', 'ffwebcomponents');
}
}
26 changes: 26 additions & 0 deletions src/Subscriber/EnrichProxyDataEventSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Omikron\FactFinder\Oxid\Subscriber;

use Omikron\FactFinder\Oxid\Event\EnrichProxyDataEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class EnrichProxyDataEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [EnrichProxyDataEvent::class => 'enrichData'];
}

public function enrichData(EnrichProxyDataEvent $event): void
{
$data = $event->getData();
$data['example_data'] = [
'some_data' => 'data_1',
'some_data2' => 'data_2',
];
$event->setData($data);
}
}
3 changes: 3 additions & 0 deletions tests/Unit/Export/Exporter/ExportEntitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class ExportEntitiesTest extends TestCase
/** @var CsvVariant */
private $stream;

/** @var string[] */
private array $columns;

protected function setUp(): void
{
$this->columns = [
Expand Down
21 changes: 1 addition & 20 deletions views/twig/extensions/themes/default/layout/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
document.addEventListener(`ffCoreReady`, ({ factfinder, init, initialSearch }) => {
init({
ff: {
url: `{{oViewConf.getFFStringConfigParam('ffServerUrl')}}`,
url: `{{communicationParams['url']}}`,
channel: `{{communicationParams['channel']}}`,
apiKey: `{{oViewConf.getFFStringConfigParam('ffApiKey')}}`,
},
Expand Down Expand Up @@ -137,25 +137,6 @@
}
}
});

{# TODO Implement proxy #}
{# {% if oViewConf.getFFBoolConfigParam('ffUseProxy') %}#}
{# factfinder.__experimental.sandboxMode.enable = true;#}

{# factfinder.eventAggregator.addBeforeDispatchingCallback(function (event) {#}
{# event.cl = 'search_result';#}
{# event.fnc = 'proxy';#}
{# });#}

{# document.addEventListener('ffUrlWrite', function (event, historyState) {#}
{# let url = event.url.replace(/([?&]?)fnc=proxy/, '');#}
{# {% if oView.getClassKey() == "alist" %}#}
{# url = url.replace(/[?&]?cl=search_result/, '');#}
{# {% endif %}#}
{# history.replaceState(historyState, "", url);#}
{# });#}
{# {% endif %}#}

</script>

{% if oView.getClassKey() == "details" %}
Expand Down
Loading