├── .github
└── workflows
│ └── continuous-integration.yml
├── .laminas-ci.json
├── .php-cs-fixer.php
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── docs
├── bookdown.json
└── projections.md
├── phpunit.xml.dist
└── src
├── AllStreamProjectionRunner.php
├── CategoryStreamProjectionRunner.php
└── MessageNameStreamProjectionRunner.php
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 | name: "Continuous Integration"
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | tags:
8 |
9 | jobs:
10 | ci:
11 | uses: laminas/workflow-continuous-integration/.github/workflows/continuous-integration.yml@1.x
12 |
--------------------------------------------------------------------------------
/.laminas-ci.json:
--------------------------------------------------------------------------------
1 | {
2 | "additional_composer_arguments": [
3 | "--prefer-stable"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | getFinder()->in(__DIR__);
5 |
6 | $config->setCacheFile(__DIR__ . '/.php_cs.cache');
7 |
8 | return $config;
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [v1.3.0](https://github.com/prooph/standard-projections/tree/v1.3.0)
4 |
5 | [Full Changelog](https://github.com/prooph/standard-projections/compare/v1.2.0...v1.3.0)
6 |
7 | **Implemented enhancements:**
8 |
9 | - Php81 compat [\#17](https://github.com/prooph/standard-projections/pull/17) ([basz](https://github.com/basz))
10 |
11 | ## [v1.2.0](https://github.com/prooph/standard-projections/tree/v1.2.0) (2022-10-03)
12 |
13 | [Full Changelog](https://github.com/prooph/standard-projections/compare/v1.1.1...v1.2.0)
14 |
15 | **Implemented enhancements:**
16 |
17 | - Laminas continuous integration [\#16](https://github.com/prooph/standard-projections/pull/16) ([basz](https://github.com/basz))
18 |
19 | **Closed issues:**
20 |
21 | - test won't run Fatal error: Class 'ProophTest\EventStore\Mock\TestDomainEvent' not found [\#12](https://github.com/prooph/standard-projections/issues/12)
22 |
23 | **Merged pull requests:**
24 |
25 | - php8 compatibility [\#15](https://github.com/prooph/standard-projections/pull/15) ([basz](https://github.com/basz))
26 | - Fixed typo in docs [\#14](https://github.com/prooph/standard-projections/pull/14) ([mikemilano](https://github.com/mikemilano))
27 | - Update cs headers [\#13](https://github.com/prooph/standard-projections/pull/13) ([basz](https://github.com/basz))
28 |
29 | ## [v1.1.1](https://github.com/prooph/standard-projections/tree/v1.1.1) (2018-02-03)
30 |
31 | [Full Changelog](https://github.com/prooph/standard-projections/compare/v1.1.0...v1.1.1)
32 |
33 | ## [v1.1.0](https://github.com/prooph/standard-projections/tree/v1.1.0) (2017-12-17)
34 |
35 | [Full Changelog](https://github.com/prooph/standard-projections/compare/v1.0.0...v1.1.0)
36 |
37 | **Implemented enhancements:**
38 |
39 | - test php 7.2 on travis [\#11](https://github.com/prooph/standard-projections/pull/11) ([prolic](https://github.com/prolic))
40 |
41 | **Merged pull requests:**
42 |
43 | - Restructure docs [\#10](https://github.com/prooph/standard-projections/pull/10) ([codeliner](https://github.com/codeliner))
44 |
45 | ## [v1.0.0](https://github.com/prooph/standard-projections/tree/v1.0.0) (2017-03-30)
46 |
47 | [Full Changelog](https://github.com/prooph/standard-projections/compare/v1.0.0-beta1...v1.0.0)
48 |
49 | **Implemented enhancements:**
50 |
51 | - phpspec/prophecy\#332 [\#7](https://github.com/prooph/standard-projections/pull/7) ([basz](https://github.com/basz))
52 | - update standard projections [\#5](https://github.com/prooph/standard-projections/pull/5) ([prolic](https://github.com/prolic))
53 | - update projection runners [\#3](https://github.com/prooph/standard-projections/pull/3) ([prolic](https://github.com/prolic))
54 | - add standard stream projections [\#1](https://github.com/prooph/standard-projections/pull/1) ([prolic](https://github.com/prolic))
55 |
56 | **Closed issues:**
57 |
58 | - Standard ReadModels [\#6](https://github.com/prooph/standard-projections/issues/6)
59 | - Use new event store api for projection creation [\#2](https://github.com/prooph/standard-projections/issues/2)
60 |
61 | **Merged pull requests:**
62 |
63 | - do not require dev versions [\#9](https://github.com/prooph/standard-projections/pull/9) ([basz](https://github.com/basz))
64 |
65 | ## [v1.0.0-beta1](https://github.com/prooph/standard-projections/tree/v1.0.0-beta1) (2016-12-13)
66 |
67 | [Full Changelog](https://github.com/prooph/standard-projections/compare/989c8fbf4e6f1fb37a01a3989cbc74e1e875317d...v1.0.0-beta1)
68 |
69 |
70 |
71 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
72 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2018, prooph software GmbH
2 | Copyright (c) 2016-2018, Sascha-Oliver Prolic
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | * Neither the name of the prooph software GmbH nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Standard Projections for Prooph EventStore
2 |
3 | [](https://github.com/prooph/standard-projections/actions/workflows/continuous-integration.yml)
4 | [](https://coveralls.io/github/prooph/standard-projections?branch=master)
5 | [](https://gitter.im/prooph/improoph)
6 |
7 | ## Overview
8 |
9 | The standard projections are some kind of event-indexing, so you can retrieve events from
10 | all streams at once (`AllStreamProjectionRunner`), by category (`CategoryStreamProjectionRunner`)
11 | or by message name (`MessageNameStreamProjectionRunner`). See docs for more information.
12 |
13 | ## Requirements
14 |
15 | - PHP >= 7.4
16 | - Prooph EventStore v7
17 |
18 | ## Documentation
19 |
20 | Documentation is [in the doc tree](docs/), and can be compiled using [bookdown](http://bookdown.io).
21 |
22 | ```console
23 | $ php ./vendor/bin/bookdown docs/bookdown.json
24 | $ php -S 0.0.0.0:8080 -t docs/html/
25 | ```
26 |
27 | Then browse to [http://localhost:8080/](http://localhost:8080/)
28 |
29 | ## Support
30 |
31 | - Ask questions on Stack Overflow tagged with [#prooph](https://stackoverflow.com/questions/tagged/prooph).
32 | - File issues at [https://github.com/prooph/event-store/issues](https://github.com/prooph/event-store/issues).
33 | - Say hello in the [prooph gitter](https://gitter.im/prooph/improoph) chat.
34 |
35 | ## Contribute
36 |
37 | Please feel free to fork and extend existing or add new plugins and send a pull request with your changes!
38 | To establish a consistent code quality, please provide unit tests for all your changes and may adapt the documentation.
39 |
40 | ## License
41 |
42 | Released under the [New BSD License](LICENSE).
43 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prooph/standard-projections",
3 | "description": "Standard Projections for Prooph EventStore",
4 | "homepage": "http://getprooph.org/",
5 | "license": "BSD-3-Clause",
6 | "authors": [
7 | {
8 | "name": "Alexander Miertsch",
9 | "email": "contact@prooph.de",
10 | "homepage": "http://www.prooph.de"
11 | },
12 | {
13 | "name": "Sascha-Oliver Prolic",
14 | "email": "saschaprolic@googlemail.com"
15 | }
16 | ],
17 | "minimum-stability": "dev",
18 | "prefer-stable": true,
19 | "require": {
20 | "php": "^7.4 || ^8.0, <8.4",
21 | "prooph/event-store": "^7.8"
22 | },
23 | "require-dev": {
24 | "php-coveralls/php-coveralls": "^2.2",
25 | "phpspec/prophecy": "^1.10.3",
26 | "phpunit/phpunit": "^9.5.5",
27 | "prooph/bookdown-template": "^0.2.3",
28 | "prooph/pdo-event-store": "^1.11",
29 | "prooph/php-cs-fixer-config": "^0.5"
30 | },
31 | "suggest": {
32 | "prooph/pdo-event-store": "^1.6 for usage with MariaDB, MySQL or Postgres as event store"
33 | },
34 | "conflict": {
35 | "sandrokeil/interop-config": "<1.0"
36 | },
37 | "autoload": {
38 | "psr-4": {
39 | "Prooph\\StandardProjections\\": "src/"
40 | }
41 | },
42 | "autoload-dev": {
43 | "psr-4": {
44 | "ProophTest\\StandardProjections\\": "tests/",
45 | "ProophTest\\EventStore\\": "vendor/prooph/event-store/tests/"
46 | }
47 | },
48 | "config": {
49 | "sort-packages": true,
50 | "preferred-install": {
51 | "prooph/*": "source"
52 | }
53 | },
54 | "scripts": {
55 | "check": [
56 | "@cs-check",
57 | "@test"
58 | ],
59 | "cs-check": "php-cs-fixer fix -v --diff --dry-run",
60 | "cs-fix": "php-cs-fixer fix -v --diff",
61 | "test": "phpunit --colors=always",
62 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/bookdown.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Standard Projections",
3 | "content": [
4 | {"overview": "projections.md"}
5 | ],
6 | "tocDepth": 1,
7 | "numbering": false,
8 | "target": "./html",
9 | "template": "../vendor/prooph/bookdown-template/templates/main.php"
10 | }
11 |
--------------------------------------------------------------------------------
/docs/projections.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | The standard projections are some kind of event-indexing, so you can retrieve events from
4 | all streams at once (`AllStreamProjectionRunner`), by category (`CategoryStreamProjectionRunner`)
5 | or by message name (`MessageNameStreamProjectionRunner`).
6 |
7 | ## Installation
8 |
9 | ```bash
10 | composer require prooph/standard-projections
11 | ```
12 |
13 | ## Requirements
14 |
15 | - PHP >= 7.1
16 | - Prooph EventStore v7
17 |
18 | ## AllStreamProjectionRunner
19 |
20 | Imagine you have two streams, a stream called `user` and a stream called `blogposts`. If you are
21 | interested in all events coming from both streams, you can use an EventStoreQuery like this:
22 |
23 | ```php
24 | $eventStore
25 | ->createQuery()
26 | ->fromAll()
27 | ```
28 |
29 | This is slightly unperformant, especially when you have one stream per aggregate, so that you have
30 | to query thousands of streams. This is where the `AllStreamProjectionRunner` comes handy. It projects
31 | all events from all streams into a single large stream, so you can run queries like:
32 |
33 | ```php
34 | $eventStore
35 | ->createQuery()
36 | ->fromStream('$all')
37 | ```
38 |
39 | ## CategoryStreamProjectionRunner
40 |
41 | Let's say you use one stream per aggregate for users. So you have event streams with names: `user-1`, `user-2` and so on.
42 | You are interested in the events from all user-streams, so your query looks like:
43 |
44 | ```php
45 | $eventStore
46 | ->createQuery()
47 | ->fromCategory('user')
48 | ```
49 |
50 | With the `CategoryStreamProjectionRunner` you create a single stream for all those events. You can query it like:
51 |
52 | ```php
53 | $eventStore
54 | ->createQuery()
55 | ->fromStream('$ct-user')
56 | ```
57 |
58 | ## MessageNameStreamProjectionRunner
59 |
60 | The `MessageNameStreamProjectionRunner` creates a stream for each occurring message name. Let's say you
61 | have user-streams with one stream per aggregate again, and streams like `user-1`, `user-2`, and so on.
62 | You are interested in all `UserWasRegistered` events, so your query looks like:
63 |
64 | ```php
65 | $eventStore
66 | ->createQuery()
67 | ->fromCategory('user')
68 | ->when(
69 | [
70 | UserWasRegistered::class => function (array $state, UserWasRegistered $event): void {
71 | // do something
72 | }
73 | ]
74 | )
75 | ```
76 |
77 | This is unperformant in two ways: First we need to query all user-streams and then we need to iterate
78 | over events, we are not interested in. With the `MessageNameStreamProjectionRunner` your query would look like:
79 |
80 | ```php
81 | $eventStore
82 | ->createQuery()
83 | ->fromStream('$mn-UserRegistered')
84 | ```
85 |
86 | ## Usage
87 |
88 | The runners are expected to run in a simple CLI script. As it's framework agnostic, you have to
89 | provide these cli-scripts yourself. This is how they basically look like:
90 |
91 | ```php
92 | get(\Prooph\EventStore\Projection\ProjectionManager::class);
97 |
98 | $runner = new \Prooph\StandardProjections\AllStreamProjectionRunner($projectionManager);
99 | $runner();
100 | ```
101 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./src
6 |
7 |
8 |
9 |
10 |
11 | ./tests
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/AllStreamProjectionRunner.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) 2016-2024 Sascha-Oliver Prolic
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Prooph\StandardProjections;
15 |
16 | use Prooph\EventStore\Projection\ProjectionManager;
17 |
18 | class AllStreamProjectionRunner
19 | {
20 | /**
21 | * @var ProjectionManager
22 | */
23 | private $projectionManager;
24 |
25 | public function __construct(ProjectionManager $projectionManager)
26 | {
27 | $this->projectionManager = $projectionManager;
28 | }
29 |
30 | public function __invoke(bool $keepRunning = true): void
31 | {
32 | $this->projectionManager
33 | ->createProjection('$all')
34 | ->fromAll()
35 | ->whenAny(function ($state, $event): void {
36 | $this->emit($event);
37 | })
38 | ->run($keepRunning);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/CategoryStreamProjectionRunner.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) 2016-2024 Sascha-Oliver Prolic
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Prooph\StandardProjections;
15 |
16 | use Prooph\EventStore\Projection\ProjectionManager;
17 |
18 | class CategoryStreamProjectionRunner
19 | {
20 | /**
21 | * @var ProjectionManager
22 | */
23 | private $projectionManager;
24 |
25 | public function __construct(ProjectionManager $projectionManager)
26 | {
27 | $this->projectionManager = $projectionManager;
28 | }
29 |
30 | public function __invoke(bool $keepRunning = true): void
31 | {
32 | $this->projectionManager
33 | ->createProjection('$by_category')
34 | ->fromAll()
35 | ->whenAny(function ($state, $event): void {
36 | $streamName = $this->streamName();
37 | $pos = \strpos($streamName, '-');
38 |
39 | if (false === $pos) {
40 | return;
41 | }
42 |
43 | $category = \substr($streamName, 0, $pos);
44 |
45 | $this->linkTo('$ct-' . $category, $event);
46 | })
47 | ->run($keepRunning);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/MessageNameStreamProjectionRunner.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) 2016-2024 Sascha-Oliver Prolic
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Prooph\StandardProjections;
15 |
16 | use Prooph\Common\Messaging\Message;
17 | use Prooph\EventStore\Projection\ProjectionManager;
18 |
19 | class MessageNameStreamProjectionRunner
20 | {
21 | /**
22 | * @var ProjectionManager
23 | */
24 | private $projectionManager;
25 |
26 | public function __construct(ProjectionManager $projectionManager)
27 | {
28 | $this->projectionManager = $projectionManager;
29 | }
30 |
31 | public function __invoke(bool $keepRunning = true): void
32 | {
33 | $this->projectionManager
34 | ->createProjection('$by_message_name')
35 | ->fromAll()
36 | ->whenAny(function ($state, Message $event): void {
37 | $messageName = $event->messageName();
38 |
39 | $this->linkTo('$mn-' . $messageName, $event);
40 | })
41 | ->run($keepRunning);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------