Skip to content

Instantly share code, notes, and snippets.

@ostrolucky
Created July 7, 2021 13:58
Show Gist options
  • Save ostrolucky/fb1adf9aa5307270eb7cc7916eb15bb5 to your computer and use it in GitHub Desktop.
Save ostrolucky/fb1adf9aa5307270eb7cc7916eb15bb5 to your computer and use it in GitHub Desktop.
Make FOSElasticsearchbundle working without doctrine
<?php
declare(strict_types=1);
namespace Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event;
use Symfony\Contracts\EventDispatcher\Event;
class AddDocumentEvent extends Event
{
private string $index;
private object $model;
public function __construct(string $index, object $model)
{
$this->index = $index;
$this->model = $model;
}
public function getIndex(): string
{
return $this->index;
}
public function getModel(): object
{
return $this->model;
}
}
<?php
declare(strict_types=1);
namespace Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\EventListener;
use Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\AddDocumentEvent;
use Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\DeleteDocumentEvent;
use FOS\ElasticaBundle\Persister\PersisterRegistry;
class BulkListener
{
private PersisterRegistry $persisterRegistry;
/** @var object[][] */
private array $toAdd = [];
/** @var int[][] */
private array $toDelete = [];
public function __construct(PersisterRegistry $persisterRegistry)
{
$this->persisterRegistry = $persisterRegistry;
}
public function onAddDocument(AddDocumentEvent $event): void
{
$this->toAdd[$event->getIndex()][] = $event->getModel();
$this->persist();
}
public function onDeleteDocument(DeleteDocumentEvent $event): void
{
$this->toDelete[$event->getIndex()][] = $event->getDocumentId();
$this->persist();
}
public function onTerminate(): void
{
$this->persist();
}
private function persist(): void
{
foreach ($this->toAdd as $indexName => $model) {
$this->persisterRegistry->getPersister($indexName)->insertMany($model);
}
$this->toAdd = [];
foreach ($this->toDelete as $indexName => $ids) {
$this->persisterRegistry->getPersister($indexName)->deleteManyByIdentifiers($ids);
}
$this->toDelete = [];
}
}
<?php
declare(strict_types=1);
namespace Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event;
use Symfony\Contracts\EventDispatcher\Event;
class DeleteDocumentEvent extends Event
{
private string $index;
private int $documentId;
public function __construct(string $index, int $documentId)
{
$this->index = $index;
$this->documentId = $documentId;
}
public function getIndex(): string
{
return $this->index;
}
public function getDocumentId(): int
{
return $this->documentId;
}
}
<?php
namespace Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch;
use Elastica\Result;
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
/**
* @template T
*/
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
/** @var class-string<T> */
private string $objectClass;
private Normalizer $denormalizer;
/** @phpstan-param class-string<T> $objectClass */
public function __construct(string $objectClass, Normalizer $denormalizer)
{
$this->objectClass = $objectClass;
$this->denormalizer = $denormalizer;
}
/** @return T[] */
public function transform(array $elasticaObjects): array
{
return $this->denormalizer->denormalize(
array_map(fn(Result $result) => $result->getSource(), $elasticaObjects),
$this->getObjectClass().'[]'
);
}
public function hybridTransform(array $elasticaObjects)
{
return $this->transform($elasticaObjects);
}
/** @return class-string<T> */
public function getObjectClass(): string
{
return $this->objectClass;
}
public function getIdentifierField(): string
{
return 'id';
}
}
<?php
declare(strict_types=1);
namespace Elektronik\Bl\DomainBundle\DependencyInjection;
use Elektronik\Bl\DomainBundle\Domain\Product\Entity\Product;
use FOS\ElasticaBundle\Persister\ObjectSerializerPersister;
use FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
class ElektronikBlDomainExtension extends Extension
{
/**
* @param mixed[] $configs
* @param ContainerBuilder $container
* @throws \Exception
*/
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$container->prependExtensionConfig('fos_elastica', [
'clients' => ['default' => ['url' => $config['elasticsearch_dsn']]],
'serializer' => null,
'indexes' => [
'product' => [
'use_alias' => true,
'finder' => null,
'serializer' => ['groups' => ['Default']],
],
],
]);
$container->register('fos_elastica.object_persister.product', ObjectSerializerPersister::class)
->setArgument('$index', new Reference('fos_elastica.index.product'))
->setArgument('$transformer', (new Definition(ModelToElasticaAutoTransformer::class))->addMethodCall('setPropertyAccessor', [new Reference('property_accessor')]),)
->setArgument('$objectClass', Product::class)
->setArgument('$serializer', [new Reference('fos_elastica.index.product.serializer.callback'), 'serialize'])
->setArgument('$options', [])
->addTag('fos_elastica.persister', ['index' => 'product']);
}
}
<?php
namespace Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch;
use Elektronik\Bl\DomainBundle\Domain\Product\Mock\ProductMock;
use FOS\ElasticaBundle\Provider\PagerfantaPager;
use FOS\ElasticaBundle\Provider\PagerInterface;
use FOS\ElasticaBundle\Provider\PagerProviderInterface;
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Pagerfanta;
class ProductProvider implements PagerProviderInterface
{
public function provide(array $options = []): PagerInterface
{
return new PagerfantaPager(new Pagerfanta(new ArrayAdapter([ProductMock::getMock()])));
}
}
<?php
declare(strict_types=1);
namespace Elektronik\Bl\DomainBundle\Infrastructure\Repository;
use Elastica\Query\Term;
use Elektronik\Bl\DomainBundle\Domain\Product\Entity\Product;
use Elektronik\Bl\DomainBundle\Domain\Product\ProductRepositoryInterface;
use Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\EntityNameEnum;
use Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\AddDocumentEvent;
use Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\DeleteDocumentEvent;
use FOS\ElasticaBundle\Finder\FinderInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class ProductRepository implements ProductRepositoryInterface
{
private FinderInterface $finder;
private EventDispatcherInterface $dispatcher;
public function __construct(
FinderInterface $finder,
EventDispatcherInterface $dispatcher
) {
$this->finder = $finder;
$this->dispatcher = $dispatcher;
}
public function save(Product $product): void
{
$this->dispatcher->dispatch(new AddDocumentEvent(
EntityNameEnum::PRODUCT,
$product
));
}
public function get(int $id): ?Product
{
return $this->finder->find((new Term())->setTerm('id', $id), 1)[0] ?? null;
}
public function remove(int $id): void
{
$this->dispatcher->dispatch(new DeleteDocumentEvent(
EntityNameEnum::PRODUCT,
$id
));
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults autowire="true" autoconfigure="true"/>
<prototype
namespace="Elektronik\Bl\DomainBundle\"
resource="../../*"
exclude="../../{DependencyInjection,Resources}"
/>
<service id="Elektronik\Bl\DomainBundle\Infrastructure\Repository\ProductRepository">
<argument key="$finder" type="service" id="fos_elastica.finder.product" />
</service>
<service id="fos_elastica.elastica_to_model_transformer.product" class="Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\ElasticaToModelTransformer">
<argument key="$objectClass" type="string">Elektronik\Bl\DomainBundle\Domain\Product\Entity\Product</argument>
</service>
<service id="Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\ProductProvider">
<tag name="fos_elastica.pager_provider" index="product" />
</service>
<!-- Elasticsearch Storage -->
<service id="bl.elasticsearch.listener.bulk" class="Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\EventListener\BulkListener">
<argument type="service" id="fos_elastica.persister_registry"/>
<tag
name="kernel.event_listener"
event="Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\AddDocumentEvent"
method="onAddDocument"/>
<tag
name="kernel.event_listener"
event="Elektronik\Bl\DomainBundle\Infrastructure\Elasticsearch\Event\DeleteDocumentEvent"
method="onDeleteDocument"/>
<tag
name="kernel.event_listener"
event="kernel.terminate"
method="onTerminate"/>
<tag
name="kernel.event_listener"
event="console.terminate"
method="onTerminate"/>
</service>
</services>
</container>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment