Skip to content

Commit

Permalink
Add synonyms
Browse files Browse the repository at this point in the history
  • Loading branch information
loevgaard committed Jul 10, 2024
1 parent e0e1934 commit 1f1c435
Show file tree
Hide file tree
Showing 36 changed files with 852 additions and 71 deletions.
1 change: 1 addition & 0 deletions src/Builder/FilterBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Symfony\Component\HttpFoundation\Request;

// todo move to Meilisearch namespace because it's related to the Meilisearch API
final class FilterBuilder implements FilterBuilderInterface
{
public function build(Request $request): array
Expand Down
35 changes: 35 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
namespace Setono\SyliusMeilisearchPlugin\DependencyInjection;

use Setono\SyliusMeilisearchPlugin\Document\Product;
use Setono\SyliusMeilisearchPlugin\Form\Type\SynonymType;
use Setono\SyliusMeilisearchPlugin\Indexer\DefaultIndexer;
use Setono\SyliusMeilisearchPlugin\Model\Synonym;
use Setono\SyliusMeilisearchPlugin\Repository\SynonymRepository;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Component\Resource\Factory\Factory;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

Expand All @@ -14,8 +20,12 @@ final class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('setono_sylius_meilisearch');

/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();

$this->addResourcesSection($rootNode);

/** @psalm-suppress MixedMethodCall,UndefinedMethod,PossiblyUndefinedMethod,PossiblyNullReference */
$rootNode
->addDefaultsIfNotSet()
Expand Down Expand Up @@ -85,4 +95,29 @@ public function getConfigTreeBuilder(): TreeBuilder

return $treeBuilder;
}

private function addResourcesSection(ArrayNodeDefinition $node): void
{
/**
* @psalm-suppress MixedMethodCall,PossiblyUndefinedMethod,PossiblyNullReference,UndefinedInterfaceMethod
*/
$node
->children()
->arrayNode('resources')
->addDefaultsIfNotSet()
->children()
->arrayNode('synonym')
->addDefaultsIfNotSet()
->children()
->variableNode('options')->end()
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue(Synonym::class)->cannotBeEmpty()->end()
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
->scalarNode('repository')->defaultValue(SynonymRepository::class)->cannotBeEmpty()->end()
->scalarNode('form')->defaultValue(SynonymType::class)->end()
->scalarNode('factory')->defaultValue(Factory::class)->end()
;
}
}
66 changes: 63 additions & 3 deletions src/DependencyInjection/SetonoSyliusMeilisearchExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@
use Setono\SyliusMeilisearchPlugin\Indexer\IndexerInterface;
use Setono\SyliusMeilisearchPlugin\Provider\IndexScope\IndexScopeProviderInterface;
use Setono\SyliusMeilisearchPlugin\UrlGenerator\EntityUrlGeneratorInterface;
use Sylius\Bundle\ResourceBundle\DependencyInjection\Extension\AbstractResourceExtension;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use function Symfony\Component\String\u;

final class SetonoSyliusMeilisearchExtension extends Extension implements PrependExtensionInterface
final class SetonoSyliusMeilisearchExtension extends AbstractResourceExtension implements PrependExtensionInterface
{
public function load(array $configs, ContainerBuilder $container): void
{
Expand All @@ -35,12 +36,15 @@ public function load(array $configs, ContainerBuilder $container): void
* @var array{
* indexes: array<string, array{document: class-string<Document>, indexer: string|null, entities: list<class-string>, prefix: string|null}>,
* server: array{ host: string, master_key: string },
* search: array{ enabled: bool, path: string, index: string, hits_per_page: integer }
* search: array{ enabled: bool, path: string, index: string, hits_per_page: integer },
* resources: array,
* } $config
*/
$config = $this->processConfiguration($this->getConfiguration([], $container), $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));

$this->registerResources('setono_sylius_meilisearch', SyliusResourceBundle::DRIVER_DOCTRINE_ORM, $config['resources'], $container);

// server
$container->setParameter('setono_sylius_meilisearch.server.host', $config['server']['host']);
$container->setParameter('setono_sylius_meilisearch.server.master_key', $config['server']['master_key']);
Expand Down Expand Up @@ -77,6 +81,62 @@ public function prepend(ContainerBuilder $container): void
],
]);

$container->prependExtensionConfig('sylius_grid', [
'grids' => [
'setono_sylius_meilisearch_admin_synonym' => [
'driver' => [
'name' => SyliusResourceBundle::DRIVER_DOCTRINE_ORM,
'options' => [
'class' => '%setono_sylius_meilisearch.model.synonym.class%',
],
],
'limits' => [100, 250, 500, 1000],
'fields' => [
'term' => [
'type' => 'string',
'label' => 'setono_sylius_meilisearch.ui.term',
],
'synonym' => [
'type' => 'string',
'label' => 'setono_sylius_meilisearch.ui.synonym',
],
'locale' => [
'type' => 'string',
'label' => 'sylius.ui.locale',
],
'channel' => [
'type' => 'string',
'label' => 'sylius.ui.channel',
],
],
'filters' => [
'search' => [
'type' => 'string',
'label' => 'sylius.ui.search',
'options' => [
'fields' => ['term', 'synonym'],
],
],
],
'actions' => [
'main' => [
'create' => [
'type' => 'create',
],
],
'item' => [
'update' => [
'type' => 'update',
],
'delete' => [
'type' => 'delete',
],
],
],
],
],
]);

$container->prependExtensionConfig('sylius_ui', [
'events' => [
'sylius.shop.layout.header.grid' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,31 @@

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\EventSubscriber\Doctrine;
namespace Setono\SyliusMeilisearchPlugin\EventListener\Doctrine;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Setono\SyliusMeilisearchPlugin\Message\Command\IndexEntity;
use Setono\SyliusMeilisearchPlugin\Message\Command\RemoveEntity;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;
use Symfony\Component\Messenger\MessageBusInterface;

final class EntityChangeSubscriber implements EventSubscriber
final class EntityListener
{
public function __construct(private readonly MessageBusInterface $commandBus)
{
}

public function getSubscribedEvents(): array
public function postPersist(LifecycleEventArgs $eventArgs): void
{
return [
Events::postPersist => 'update',
Events::postUpdate => 'update',
Events::postRemove => 'remove',
];
$this->dispatch($eventArgs, static fn (IndexableInterface $entity) => IndexEntity::new($entity));
}

public function update(LifecycleEventArgs $eventArgs): void
public function postUpdate(LifecycleEventArgs $eventArgs): void
{
$this->dispatch($eventArgs, static fn (IndexableInterface $entity) => IndexEntity::new($entity));
}

public function remove(LifecycleEventArgs $eventArgs): void
public function postRemove(LifecycleEventArgs $eventArgs): void
{
$this->dispatch($eventArgs, static fn (IndexableInterface $entity) => RemoveEntity::new($entity));
}
Expand Down
41 changes: 41 additions & 0 deletions src/EventListener/Doctrine/SynonymListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\EventListener\Doctrine;

use Doctrine\Persistence\Event\LifecycleEventArgs;
use Setono\SyliusMeilisearchPlugin\Message\Command\UpdateSynonyms;
use Setono\SyliusMeilisearchPlugin\Model\SynonymInterface;
use Symfony\Component\Messenger\MessageBusInterface;

final class SynonymListener
{
public function __construct(private readonly MessageBusInterface $commandBus)
{
}

public function postPersist(LifecycleEventArgs $eventArgs): void
{
$this->handle($eventArgs);
}

public function postUpdate(LifecycleEventArgs $eventArgs): void
{
$this->handle($eventArgs);
}

public function postRemove(LifecycleEventArgs $eventArgs): void
{
$this->handle($eventArgs);
}

public function handle(LifecycleEventArgs $eventArgs): void
{
if (!$eventArgs->getObject() instanceof SynonymInterface) {
return;
}

$this->commandBus->dispatch(new UpdateSynonyms());
}
}
55 changes: 55 additions & 0 deletions src/EventSubscriber/AddMenuSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\EventSubscriber;

use Knp\Menu\ItemInterface;
use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class AddMenuSubscriber implements EventSubscriberInterface
{
public const MENU_ITEM_KEY = 'setono_sylius_meilisearch';

public static function getSubscribedEvents(): array
{
return [
'sylius.menu.admin.main' => 'add',
];
}

public function add(MenuBuilderEvent $event): void
{
$menu = $event->getMenu();

$header = $this->getHeader($menu);

$header
->addChild('synonyms', [
'route' => 'setono_sylius_meilisearch_admin_synonym_index',
])
->setLabel('setono_sylius_meilisearch.menu.admin.main.meilisearch.synonyms')
->setLabelAttribute('icon', 'exchange')
;

$order = ['catalog', 'sales', 'customers', 'marketing', self::MENU_ITEM_KEY];
$rest = array_diff(array_keys($menu->getChildren()), $order);

try {
$event->getMenu()->reorderChildren(array_merge($order, $rest));
} catch (\InvalidArgumentException) {
}
}

private function getHeader(ItemInterface $menu): ItemInterface
{
$header = $menu->getChild(self::MENU_ITEM_KEY);
if (null !== $header) {
return $header;
}

return $menu->addChild(self::MENU_ITEM_KEY)
->setLabel('setono_sylius_meilisearch.menu.admin.main.meilisearch.header');
}
}
54 changes: 54 additions & 0 deletions src/EventSubscriber/NewSynonymSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\EventSubscriber;

use Doctrine\Persistence\ManagerRegistry;
use Setono\Doctrine\ORMTrait;
use Setono\SyliusMeilisearchPlugin\Factory\SynonymFactoryInterface;
use Setono\SyliusMeilisearchPlugin\Model\SynonymInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Exception\OutOfBoundsException;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Webmozart\Assert\Assert;

final class NewSynonymSubscriber implements EventSubscriberInterface
{
use ORMTrait;

public function __construct(ManagerRegistry $managerRegistry, private readonly SynonymFactoryInterface $synonymFactory)
{
$this->managerRegistry = $managerRegistry;
}

public static function getSubscribedEvents(): array
{
return [
FormEvents::POST_SUBMIT => 'handleFormEvent',
];
}

public function handleFormEvent(FormEvent $event): void
{
try {
$direction = $event->getForm()->get('direction')->getData();
} catch (OutOfBoundsException) {
return;
}

// todo replace with constant
if ('two_way' !== $direction) {
return;
}

/** @var mixed $synonym */
$synonym = $event->getData();
Assert::isInstanceOf($synonym, SynonymInterface::class);

$newSynonym = $this->synonymFactory->createInverseFromExisting($synonym);

$this->getManager($newSynonym)->persist($newSynonym);
}
}
Loading

0 comments on commit 1f1c435

Please sign in to comment.