diff --git a/docs/interop_factories.md b/docs/interop_factories.md index 39c96ff..989bfc0 100644 --- a/docs/interop_factories.md +++ b/docs/interop_factories.md @@ -96,6 +96,24 @@ You can also configure a custom stream name (default is `event_stream`): ] ``` +You can add your custom event store too (default is `EventStore::class`): +```php +[ + 'prooph' => [ + 'event_sourcing' => [ + 'aggregate_repository' => [ + 'user_repository' => [ + 'repository_class' => MyUserRepository::class, + 'event_store' => MyCustomEventStore::class, // <-- Custom event store service id + 'aggregate_type' => MyUser::class, + 'aggregate_translator' => 'user_translator', + ], + ], + ], + ], +] +``` + Last but not least you can enable the so called "One-Stream-Per-Aggregate-Mode": ```php [ diff --git a/src/Container/Aggregate/AggregateRepositoryFactory.php b/src/Container/Aggregate/AggregateRepositoryFactory.php index 18b3ff1..378ab00 100644 --- a/src/Container/Aggregate/AggregateRepositoryFactory.php +++ b/src/Container/Aggregate/AggregateRepositoryFactory.php @@ -13,6 +13,7 @@ namespace Prooph\EventSourcing\Container\Aggregate; use Interop\Config\ConfigurationTrait; +use Interop\Config\ProvidesDefaultOptions; use Interop\Config\RequiresConfigId; use Interop\Config\RequiresMandatoryOptions; use InvalidArgumentException; @@ -23,7 +24,7 @@ use Prooph\EventStore\StreamName; use Psr\Container\ContainerInterface; -final class AggregateRepositoryFactory implements RequiresConfigId, RequiresMandatoryOptions +final class AggregateRepositoryFactory implements RequiresConfigId, RequiresMandatoryOptions, ProvidesDefaultOptions { use ConfigurationTrait; @@ -81,7 +82,7 @@ public function __invoke(ContainerInterface $container): AggregateRepository throw ConfigurationException::configurationError(sprintf('Repository class %s must be a sub class of %s', $repositoryClass, AggregateRepository::class)); } - $eventStore = $container->get(EventStore::class); + $eventStore = $container->get($config['event_store']); if (is_array($config['aggregate_type'])) { $aggregateType = AggregateType::fromMapping($config['aggregate_type']); @@ -120,4 +121,16 @@ public function mandatoryOptions(): iterable 'aggregate_translator', ]; } + + /** + * Returns a list of default options, which are merged in \Interop\Config\RequiresConfig::options() + * + * @return iterable List with default options and values, can be nested + */ + public function defaultOptions(): iterable + { + return [ + 'event_store' => EventStore::class, + ]; + } } diff --git a/tests/Container/Aggregate/AggregateRepositoryFactoryTest.php b/tests/Container/Aggregate/AggregateRepositoryFactoryTest.php index 80d0028..3e1f393 100644 --- a/tests/Container/Aggregate/AggregateRepositoryFactoryTest.php +++ b/tests/Container/Aggregate/AggregateRepositoryFactoryTest.php @@ -17,6 +17,7 @@ use Prooph\EventSourcing\Container\Aggregate\AggregateRepositoryFactory; use Prooph\EventStore\EventStore; use Prooph\EventStore\Exception\ConfigurationException; +use ProophTest\EventSourcing\Mock\EventStoreMock; use ProophTest\EventSourcing\Mock\RepositoryMock; use ProophTest\EventStore\ActionEventEmitterEventStoreTestCase; use ProophTest\EventStore\Mock\User; @@ -54,6 +55,37 @@ public function it_creates_an_aggregate_from_static_call(): void self::assertInstanceOf(RepositoryMock::class, $factory($container->reveal())); } + /** + * @test + */ + public function it_creates_an_aggregate_from_static_call_with_custom_event_store(): void + { + $container = $this->prophesize(ContainerInterface::class); + $container->has('config')->willReturn(true); + $container->get('config')->willReturn([ + 'prooph' => [ + 'event_sourcing' => [ + 'aggregate_repository' => [ + 'repository_mock' => [ + 'repository_class' => RepositoryMock::class, + 'event_store' => EventStoreMock::class, + 'aggregate_type' => User::class, + 'aggregate_translator' => 'user_translator', + ], + ], + ], + ], + ]); + $container->get(EventStoreMock::class)->willReturn($this->eventStore); + + $userTranslator = $this->prophesize(AggregateTranslator::class); + + $container->get('user_translator')->willReturn($userTranslator->reveal()); + + $factory = [AggregateRepositoryFactory::class, 'repository_mock']; + self::assertInstanceOf(RepositoryMock::class, $factory($container->reveal())); + } + /** * @test */ @@ -119,6 +151,39 @@ public function it_throws_exception_when_invalid_repository_class_given(): void $factory->__invoke($container->reveal()); } + /** + * @test + */ + public function it_throws_exception_when_invalid_event_store_class_given(): void + { + $this->expectException(\TypeError::class); + + $container = $this->prophesize(ContainerInterface::class); + $container->has('config')->willReturn(true); + $container->get('config')->willReturn([ + 'prooph' => [ + 'event_sourcing' => [ + 'aggregate_repository' => [ + 'repository_mock' => [ + 'repository_class' => RepositoryMock::class, + 'event_store' => 'stdClass', + 'aggregate_type' => User::class, + 'aggregate_translator' => 'user_translator', + ], + ], + ], + ], + ]); + $container->get('stdClass')->willReturn('stdClass'); + + $userTranslator = $this->prophesize(AggregateTranslator::class); + + $container->get('user_translator')->willReturn($userTranslator->reveal()); + + $factory = [AggregateRepositoryFactory::class, 'repository_mock']; + self::assertInstanceOf(RepositoryMock::class, $factory($container->reveal())); + } + /** * @test */ diff --git a/tests/Mock/EventStoreMock.php b/tests/Mock/EventStoreMock.php new file mode 100644 index 0000000..1719f0f --- /dev/null +++ b/tests/Mock/EventStoreMock.php @@ -0,0 +1,106 @@ + + * (c) 2015-2018 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ProophTest\EventSourcing\Mock; + +use Iterator; +use Prooph\EventStore\EventStore; +use Prooph\EventStore\Metadata\MetadataMatcher; +use Prooph\EventStore\Stream; +use Prooph\EventStore\StreamName; + +final class EventStoreMock implements EventStore +{ + public function updateStreamMetadata(StreamName $streamName, array $newMetadata): void + { + } + + public function create(Stream $stream): void + { + } + + public function appendTo(StreamName $streamName, Iterator $streamEvents): void + { + } + + public function delete(StreamName $streamName): void + { + } + + public function fetchStreamMetadata(StreamName $streamName): array + { + return []; + } + + public function hasStream(StreamName $streamName): bool + { + return true; + } + + public function load( + StreamName $streamName, + int $fromNumber = 1, + int $count = null, + MetadataMatcher $metadataMatcher = null + ): Iterator { + return new \ArrayIterator(); + } + + public function loadReverse( + StreamName $streamName, + int $fromNumber = null, + int $count = null, + MetadataMatcher $metadataMatcher = null + ): Iterator { + return new \ArrayIterator(); + } + + /** + * @return StreamName[] + */ + public function fetchStreamNames( + ?string $filter, + ?MetadataMatcher $metadataMatcher, + int $limit = 20, + int $offset = 0 + ): array { + return []; + } + + /** + * @return StreamName[] + */ + public function fetchStreamNamesRegex( + string $filter, + ?MetadataMatcher $metadataMatcher, + int $limit = 20, + int $offset = 0 + ): array { + return []; + } + + /** + * @return string[] + */ + public function fetchCategoryNames(?string $filter, int $limit = 20, int $offset = 0): array + { + return []; + } + + /** + * @return string[] + */ + public function fetchCategoryNamesRegex(string $filter, int $limit = 20, int $offset = 0): array + { + return []; + } +}