Skip to content

Commit

Permalink
Add normalizer to index data provider to factorize code
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreGauthier committed Nov 8, 2024
1 parent 136840e commit 825030d
Show file tree
Hide file tree
Showing 14 changed files with 800 additions and 368 deletions.
2 changes: 0 additions & 2 deletions src/DependencyInjection/GallyOroExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ class GallyOroExtension extends Extension
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
// register services configuration
$loader->load('services.yml');
// register other configurations in the same way
}
}
358 changes: 0 additions & 358 deletions src/Engine/IndexDataProvider.php

This file was deleted.

7 changes: 6 additions & 1 deletion src/Engine/SearchEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,14 @@ protected function doSearch(Query $query, array $context = [])

$results = [];
foreach ($response->getCollection() as $item) {
$item['id'] = (int) basename($item['id']);
$item['system_entity_id'] = $item['id']; // todo manage attributes
$item['names'] = $item['name'];
$item['descriptions'] = $item['description'] ?? '';

$results[] = new Item(
'product', // Todo manage other entity
basename($item['id']),
$item['id'],
$item['url'] ?? null,
$this->mapper->mapSelectedData($query, $item),
$this->mappingProvider->getEntityConfig('product')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/**
* Add child data to product data.
*/
class WebsiteSearchVariantDataIndexerListener implements WebsiteSearchProductIndexerListenerInterface
class WebsiteSearchChildDataIndexerListener implements WebsiteSearchProductIndexerListenerInterface
{
use ContextTrait;

Expand Down
190 changes: 190 additions & 0 deletions src/Indexer/IndexDataProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
*
* @package Gally
* @author Gally Team <[email protected]>
* @copyright 2024-present Smile
* @license Open Software License v. 3.0 (OSL-3.0)
*/

declare(strict_types=1);

namespace Gally\OroPlugin\Indexer;

use Doctrine\ORM\EntityManagerInterface;
use Gally\OroPlugin\Engine\Indexer;
use Gally\OroPlugin\Indexer\Normalizer\AbstractNormalizer;
use Oro\Bundle\ConfigBundle\Config\ConfigManager;
use Oro\Bundle\EntityBundle\ORM\DoctrineHelper;
use Oro\Bundle\EntityBundle\ORM\EntityAliasResolver;
use Oro\Bundle\LocaleBundle\Entity\Localization;
use Oro\Bundle\LocaleBundle\Helper\LocalizationHelper;
use Oro\Bundle\SearchBundle\Provider\SearchMappingProvider;
use Oro\Bundle\UIBundle\Tools\HtmlTagHelper;
use Oro\Bundle\WebsiteSearchBundle\Engine\Context\ContextTrait;
use Oro\Bundle\WebsiteSearchBundle\Engine\IndexDataProvider as BaseIndexDataProvider;
use Oro\Bundle\WebsiteSearchBundle\Event;
use Oro\Bundle\WebsiteSearchBundle\Helper\PlaceholderHelper;
use Oro\Bundle\WebsiteSearchBundle\Manager\WebsiteContextManager;
use Oro\Bundle\WebsiteSearchBundle\Placeholder\LocalizationIdPlaceholder;
use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderInterface;
use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderValue;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
* Class is responsible for triggering all events during indexation
* and returning all collected and prepared for saving event data.
*/
class IndexDataProvider extends BaseIndexDataProvider
{
use ContextTrait;

/**
* @param iterable<AbstractNormalizer> $normalizers
*/
public function __construct(
private EventDispatcherInterface $eventDispatcher,
private EntityAliasResolver $entityAliasResolver,
private PlaceholderInterface $placeholder,
HtmlTagHelper $htmlTagHelper,
PlaceholderHelper $placeholderHelper,
private DoctrineHelper $doctrineHelper,
private LocalizationHelper $localizationHelper,
private WebsiteContextManager $websiteContextManager,
private ConfigManager $configManager,
private SearchMappingProvider $mappingProvider,
private EntityManagerInterface $entityManager,
private array $attributeMapping,
private iterable $normalizers,
) {
parent::__construct($eventDispatcher, $entityAliasResolver, $placeholder, $htmlTagHelper, $placeholderHelper);
}

/**
* @param string $entityClass
* @param object[] $restrictedEntities
* @param array $context
* $context = [
* 'currentWebsiteId' int Current website id. Should not be passed manually. It is computed from 'websiteIds'
* ]
*
* @return array
*/
public function getEntitiesData($entityClass, array $restrictedEntities, array $context, array $entityConfig)
{
$entityAlias = $this->entityAliasResolver->getAlias($entityClass);

$indexEntityEvent = new Event\IndexEntityEvent($entityClass, $restrictedEntities, $context);
$this->eventDispatcher->dispatch($indexEntityEvent, Event\IndexEntityEvent::NAME);
$this->eventDispatcher->dispatch(
$indexEntityEvent,
sprintf('%s.%s', Event\IndexEntityEvent::NAME, $entityAlias)
);

return $this->prepareIndexData($entityClass, $indexEntityEvent->getEntitiesData(), $entityConfig, $context);
}

/**
* Adds field types according to entity config, applies placeholders.
*
* @return array Structured and cleared data ready to be saved
*/
private function prepareIndexData(
string $entityClass,
array $indexData,
array $entityConfig,
array $context
): array {
$preparedIndexData = [];

/** @var Localization $localization */
$localization = $context[Indexer::CONTEXT_LOCALIZATION];
$website = $this->websiteContextManager->getWebsite($context);

foreach ($this->normalizers as $normalizer) {
$normalizer->preProcess($website, $localization, $entityClass, $entityConfig, $indexData);
}

foreach ($indexData as $entityId => $fieldsValues) {
$preparedIndexData[$entityId] = $preparedIndexData[$entityId] ?? [];

foreach ($this->normalizers as $normalizer) {
$normalizer->normalize(
$website,
$entityClass,
$entityId,
$fieldsValues,
$preparedIndexData[$entityId],
);
}

foreach ($this->toArray($fieldsValues) as $fieldName => $values) {
$singleValueFieldName = $this->cleanFieldName((string) $fieldName);
foreach ($this->toArray($values) as $value) {
$value = $value['value'];
$placeholders = [];

if ($value instanceof PlaceholderValue) {
$placeholders = $value->getPlaceholders();
$value = $value->getValue();
}

if (\array_key_exists(LocalizationIdPlaceholder::NAME, $placeholders)) {
if ($localization->getId() != $placeholders[LocalizationIdPlaceholder::NAME]) {
continue;
}
}

if (str_starts_with($fieldName, 'category_path')) {
// Todo
} elseif (str_starts_with($fieldName, 'ordered_at_by')) {
// Todo
} elseif (\in_array($fieldName, ['featured', 'is_variant', 'newArrival', 'is_upcoming', 'is_visible_by_default'], true)) { // todo detect boolean
$preparedIndexData[$entityId][$fieldName] = (bool) $value;
} elseif (!str_starts_with($fieldName, self::ALL_TEXT_PREFIX)) {
if (null === $value || '' === $value || [] === $value) {
continue;
}
$singleValueFieldName = $this->placeholder->replace($singleValueFieldName, $placeholders);
$preparedIndexData[$entityId][$singleValueFieldName] = $value;
}
}
}

$preparedIndexData[$entityId] = $preparedIndexData[$entityId] ?? [];

$preparedIndexData[$entityId]['id'] = (string) $entityId;
if (\array_key_exists('image_product_medium', $preparedIndexData[$entityId])) {
$preparedIndexData[$entityId]['image'] = $preparedIndexData[$entityId]['image_product_medium'];
}
}

foreach ($this->normalizers as $normalizer) {
$normalizer->postProcess($website, $entityClass, $preparedIndexData);
}

return $preparedIndexData;
}

private function cleanFieldName(string $fieldName): string
{
$fieldName = trim(
$this->placeholder->replace($fieldName, [LocalizationIdPlaceholder::NAME => null]),
'_.'
);

return $this->attributeMapping[$fieldName] ?? $fieldName;
}

private function toArray($value): array
{
if (\is_array($value) && !\array_key_exists('value', $value)) {
return $value;
}

return [$value];
}
}
55 changes: 55 additions & 0 deletions src/Indexer/Normalizer/AbstractNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
*
* @package Gally
* @author Gally Team <[email protected]>
* @copyright 2024-present Smile
* @license Open Software License v. 3.0 (OSL-3.0)
*/

declare(strict_types=1);

namespace Gally\OroPlugin\Indexer\Normalizer;

use Oro\Bundle\LocaleBundle\Entity\Localization;
use Oro\Bundle\WebsiteBundle\Entity\Website;

abstract class AbstractNormalizer
{
public function preProcess(
Website $website,
Localization $localization,
string $entityClass,
array $entityConfig,
array &$indexData,
): void {
}

public function normalize(
Website $website,
string $entityClass,
string|int $entityId,
array &$fieldsValues,
array &$preparedEntityData,
): void {
}

public function postProcess(
Website $website,
string $entityClass,
array &$preparedIndexData,
): void {
}

protected function toArray($value): array
{
if (\is_array($value) && !\array_key_exists('value', $value)) {
return $value;
}

return [$value];
}
}
57 changes: 57 additions & 0 deletions src/Indexer/Normalizer/BrandDataNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
*
* @package Gally
* @author Gally Team <[email protected]>
* @copyright 2024-present Smile
* @license Open Software License v. 3.0 (OSL-3.0)
*/

declare(strict_types=1);

namespace Gally\OroPlugin\Indexer\Normalizer;

use Oro\Bundle\LocaleBundle\Entity\Localization;
use Oro\Bundle\ProductBundle\Entity\Product;
use Oro\Bundle\WebsiteBundle\Entity\Website;

class BrandDataNormalizer extends AbstractNormalizer
{
public function preProcess(
Website $website,
Localization $localization,
string $entityClass,
array $entityConfig,
array &$indexData,
): void {
if (Product::class === $entityClass) {
foreach ($indexData as &$fieldsValues) {
if (isset($fieldsValues['brand_LOCALIZATION_ID'])) {
$fieldsValues['brand_name'] = $fieldsValues['brand_LOCALIZATION_ID'];
unset($fieldsValues['brand_LOCALIZATION_ID']);
}
}
}
}

public function postProcess(
Website $website,
string $entityClass,
array &$preparedIndexData,
): void {
if (Product::class === $entityClass) {
foreach ($preparedIndexData as $entityId => &$data) {
if (isset($data['brand'])) {
$preparedIndexData[$entityId]['brand'] = [[
'value' => $data['brand'],
'label' => $data['brand_name'],
]];
}
unset($preparedIndexData[$entityId]['brand_name']);
}
}
}
}
Loading

0 comments on commit 825030d

Please sign in to comment.