Skip to content
Open
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
22 changes: 22 additions & 0 deletions src/DependencyInjection/FOSElasticaExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
use FOS\ElasticaBundle\Finder\TransformedFinder;
use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Messenger\MessageBusInterface;

/**
Expand Down Expand Up @@ -67,6 +70,13 @@ class FOSElasticaExtension extends Extension
*/
private $loadedDrivers = [];

/**
* Custom repository service references keyed by index name.
*
* @var array<string, ServiceClosureArgument>
*/
private $customRepositories = [];

public function load(array $configs, ContainerBuilder $container): void
{
$configuration = $this->getConfiguration($configs, $container);
Expand Down Expand Up @@ -134,6 +144,10 @@ public function load(array $configs, ContainerBuilder $container): void
$this->loadIndexManager($container);
$this->loadIndexTemplateManager($container);

$container->getDefinition('fos_elastica.repository_manager')
->replaceArgument(0, new Definition(ServiceLocator::class, [$this->customRepositories]))
;

$this->createDefaultManagerAlias($config['default_manager'], $container);
}

Expand Down Expand Up @@ -710,6 +724,14 @@ private function loadTypeFinder(array $typeConfig, ContainerBuilder $container,
$arguments = [$indexName, new Reference($finderId)];
if (isset($typeConfig['repository'])) {
$arguments[] = $typeConfig['repository'];

$repositoryId = \sprintf('fos_elastica.repository.%s', $indexName);
$repositoryDef = new Definition($typeConfig['repository']);
$repositoryDef->setArgument('$finder', new Reference($finderId));
$repositoryDef->setAutowired(true);
$container->setDefinition($repositoryId, $repositoryDef);

$this->customRepositories[$indexName] = new ServiceClosureArgument(new Reference($repositoryId));
}

$container->getDefinition('fos_elastica.repository_manager')
Expand Down
21 changes: 18 additions & 3 deletions src/Manager/RepositoryManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
namespace FOS\ElasticaBundle\Manager;

use FOS\ElasticaBundle\Finder\FinderInterface;
use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
use FOS\ElasticaBundle\Repository;
use Psr\Container\ContainerInterface;

/**
* @author Richard Miller <info@limethinking.co.uk>
Expand All @@ -32,6 +34,13 @@ class RepositoryManager implements RepositoryManagerInterface
*/
private $repositories = [];

private ContainerInterface $repositoryLocator;

public function __construct(ContainerInterface $repositoryLocator)
{
$this->repositoryLocator = $repositoryLocator;
}

public function addIndex(string $indexName, FinderInterface $finder, ?string $repositoryName = null): void
{
$this->indexes[$indexName] = [
Expand Down Expand Up @@ -80,10 +89,16 @@ protected function getRepositoryName(string $indexName): string
*/
private function createRepository(string $indexName)
{
if (!\class_exists($repositoryName = $this->getRepositoryName($indexName))) {
throw new \RuntimeException(\sprintf('%s repository for index "%s" does not exist', $repositoryName, $indexName));
if ($this->repositoryLocator->has($indexName)) {
return $this->repositoryLocator->get($indexName);
}

$finder = $this->indexes[$indexName]['finder'];

if (!$finder instanceof PaginatedFinderInterface) {
throw new \RuntimeException(\sprintf('Finder for index "%s" must implement PaginatedFinderInterface', $indexName));
}

return new $repositoryName($this->indexes[$indexName]['finder']);
return new Repository($finder);
}
}
4 changes: 3 additions & 1 deletion src/Resources/config/index.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="fos_elastica.repository_manager" class="FOS\ElasticaBundle\Manager\RepositoryManager" />
<service id="fos_elastica.repository_manager" class="FOS\ElasticaBundle\Manager\RepositoryManager">
<argument /> <!-- repository service locator, replaced in FOSElasticaExtension -->
</service>

<service id="fos_elastica.alias_processor" class="FOS\ElasticaBundle\Index\AliasProcessor" />

Expand Down
97 changes: 97 additions & 0 deletions tests/Unit/DependencyInjection/FOSElasticaExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
use FOS\ElasticaBundle\Persister\PagerPersisterRegistry;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Yaml\Yaml;

/**
Expand Down Expand Up @@ -537,4 +540,98 @@ public function testIndexTemplates()
);
$this->assertTrue($container->hasDefinition('fos_elastica.index_template.some_index_template'));
}

public function testShouldRegisterCustomRepositoryAsService()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', true);

$extension = new FOSElasticaExtension();
$extension->load(
[
'fos_elastica' => [
'clients' => [
'default' => ['hosts' => ['a_host:a_port']],
],
'indexes' => [
'acme_index' => [
'persistence' => [
'driver' => 'orm',
'model' => 'theModelClass',
'provider' => null,
'listener' => null,
'finder' => null,
'repository' => 'App\Repository\CustomElasticaRepository',
],
'properties' => ['text' => null],
],
],
],
],
$container
);

// Custom repository is registered as a DI service
$this->assertTrue($container->hasDefinition('fos_elastica.repository.acme_index'));

$repositoryDefinition = $container->getDefinition('fos_elastica.repository.acme_index');
$this->assertSame('App\Repository\CustomElasticaRepository', $repositoryDefinition->getClass());
$this->assertTrue($repositoryDefinition->isAutowired());

// Finder is injected into the custom repository
$finderArgument = $repositoryDefinition->getArgument('$finder');
$this->assertInstanceOf(Reference::class, $finderArgument);
$this->assertSame('fos_elastica.finder.acme_index', (string) $finderArgument);

// Repository manager receives a ServiceLocator with the custom repository
$repositoryManagerDef = $container->getDefinition('fos_elastica.repository_manager');
$locatorDefinition = $repositoryManagerDef->getArgument(0);
$this->assertInstanceOf(Definition::class, $locatorDefinition);
$this->assertSame(ServiceLocator::class, $locatorDefinition->getClass());

$locatorArguments = $locatorDefinition->getArguments();
$this->assertArrayHasKey(0, $locatorArguments);
$this->assertArrayHasKey('acme_index', $locatorArguments[0]);
$this->assertInstanceOf(ServiceClosureArgument::class, $locatorArguments[0]['acme_index']);
}

public function testShouldNotRegisterRepositoryServiceWhenNoCustomRepository()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', true);

$extension = new FOSElasticaExtension();
$extension->load(
[
'fos_elastica' => [
'clients' => [
'default' => ['hosts' => ['a_host:a_port']],
],
'indexes' => [
'acme_index' => [
'persistence' => [
'driver' => 'orm',
'model' => 'theModelClass',
'provider' => null,
'listener' => null,
'finder' => null,
],
'properties' => ['text' => null],
],
],
],
],
$container
);

// No custom repository service should be registered
$this->assertFalse($container->hasDefinition('fos_elastica.repository.acme_index'));

// ServiceLocator should be empty
$repositoryManagerDef = $container->getDefinition('fos_elastica.repository_manager');
$locatorDefinition = $repositoryManagerDef->getArgument(0);
$this->assertInstanceOf(Definition::class, $locatorDefinition);
$locatorArguments = $locatorDefinition->getArguments();
$this->assertEmpty($locatorArguments[0]);
}
}
64 changes: 55 additions & 9 deletions tests/Unit/Manager/RepositoryManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use FOS\ElasticaBundle\Manager\RepositoryManager;
use FOS\ElasticaBundle\Repository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ServiceLocator;

class CustomRepository extends Repository
{
Expand All @@ -37,22 +38,47 @@ public function testThatGetRepositoryReturnsDefaultRepository()

$indexName = 'index';

$manager = new RepositoryManager();
$manager = new RepositoryManager(new ServiceLocator([]));
$manager->addIndex($indexName, $finderMock);
$repository = $manager->getRepository($indexName);
$this->assertInstanceOf(Repository::class, $repository);
}

public function testThatGetRepositoryReturnsCustomRepository()
public function testThatGetRepositoryReturnsCustomRepositoryFromLocator()
{
$finderMock = $this->createMock(TransformedFinder::class);
$customRepository = new CustomRepository($finderMock);

$indexName = 'index';

$manager = new RepositoryManager();
$locator = new ServiceLocator([
$indexName => static fn () => $customRepository,
]);

$manager = new RepositoryManager($locator);
$manager->addIndex($indexName, $finderMock, CustomRepository::class);
$repository = $manager->getRepository($indexName);
$this->assertInstanceOf(CustomRepository::class, $repository);
$this->assertSame($customRepository, $repository);
}

public function testThatGetRepositoryReturnsCachedRepository()
{
$finderMock = $this->createMock(TransformedFinder::class);
$customRepository = new CustomRepository($finderMock);

$indexName = 'index';

$locator = new ServiceLocator([
$indexName => static fn () => $customRepository,
]);

$manager = new RepositoryManager($locator);
$manager->addIndex($indexName, $finderMock, CustomRepository::class);

$first = $manager->getRepository($indexName);
$second = $manager->getRepository($indexName);
$this->assertSame($first, $second);
}

public function testThatGetRepositoryThrowsExceptionIfEntityNotConfigured()
Expand All @@ -61,23 +87,43 @@ public function testThatGetRepositoryThrowsExceptionIfEntityNotConfigured()

$indexName = 'index';

$manager = new RepositoryManager();
$manager = new RepositoryManager(new ServiceLocator([]));
$manager->addIndex($indexName, $finderMock);

$this->expectException(\RuntimeException::class);
$manager->getRepository('Missing type');
}

public function testThatGetRepositoryThrowsExceptionIfCustomRepositoryNotFound()
public function testThatLocatorIsPreferredOverDefaultRepository()
{
$finderMock = $this->createMock(TransformedFinder::class);
$customRepository = new CustomRepository($finderMock);

$indexName = 'index';

$manager = new RepositoryManager();
$manager->addIndex($indexName, $finderMock, 'FOS\ElasticaBundle\Tests\MissingRepository');
$locator = new ServiceLocator([
$indexName => static fn () => $customRepository,
]);

$this->expectException(\RuntimeException::class);
$manager->getRepository($indexName);
$manager = new RepositoryManager($locator);
// Even without specifying a custom repository name, locator takes precedence
$manager->addIndex($indexName, $finderMock);
$repository = $manager->getRepository($indexName);
$this->assertSame($customRepository, $repository);
}

public function testThatDefaultRepositoryIsReturnedWhenNotInLocator()
{
$finderMock = $this->createMock(TransformedFinder::class);

$locator = new ServiceLocator([
'other_index' => static fn () => new CustomRepository($finderMock),
]);

$manager = new RepositoryManager($locator);
$manager->addIndex('my_index', $finderMock);
$repository = $manager->getRepository('my_index');
$this->assertInstanceOf(Repository::class, $repository);
$this->assertNotInstanceOf(CustomRepository::class, $repository);
}
}
Loading