symfony messenger multiple bus - symfony

I need to have multiple bus in my application written on Symfony. To do so I'm using messenger bundle.
So I need to have:
external_events_bus - which receiving messages from external systems, which using sqs transport
application_events_bus - for application events (using database driver for now)
domain_events_bus - for domain events (using database driver for now)
command_bus - for commands and queries (CQRS) (currently sync)
so in my messenger.yaml I have:
framework:
messenger:
default_bus: command.bus
buses:
command.bus: ~
domain_event.bus: ~
application_event.bus: ~
external_event.bus: ~
transports:
command_bus: '%env(COMMAND_BUS_DSN)%'
internal_event_bus: '%env(INTERNAL_EVENT_BUS_DSN)%'
external_event_bus: '%(EXTERNAL_EVENT_BUS_DSN)%'
routing:
'App\Application\CommandBus\Command': command_bus
'App\Domain\EventBus\DomainEvent': internal_event_bus
'App\Application\EventBus\ApplicationEvent': internal_event_bus
'App\Infrastructure\ExternalEventBus\ExternalEvent': external_event_bus
and in my services.yaml
services:
_defaults:
autowire: true
autoconfigure: true subscribers, etc.
public: false
_instanceof:
# use tags to avoid coupling handler classes to Messenger's interface
App\Application\CommandBus\CommandHandler:
tags: [ { name: messenger.message_handler, bus: command.bus } ]
App\Interfaces\ExternalEventBus\ExternalEventHandler:
tags: [ { name: messenger.message_handler, bus: external_event.bus } ]
App\Domain\EventBus\DomainEventHandler:
tags: [ { name: messenger.message_handler, bus: domain_event.bus } ]
App\Application\EventBus\ApplicationEventHandler:
tags: [ { name: messenger.message_handler, bus: application_event.bus } ]
... // some other services here
App\Application\CommandBus\CommandBus:
class: App\Infrastructure\CommandBus\CommandBus
arguments: [ "#command.bus" ]
App\Domain\EventBus\DomainEventBus:
class: App\Infrastructure\DomainEventBus\DomainEventBus
arguments: [ "#domain_event.bus" ]
App\Application\EventBus\ApplicationEventBus:
class: App\Infrastructure\ApplicationEventBus\ApplicationEventBus
arguments: [ "#application_event.bus" ]
App\Infrastructure\ExternalEventBus\ExternalEventBus:
class: App\Infrastructure\ExternalEventBus\ExternalEventBus
arguments: [ "#external_event.bus" ]
Want to clarify before I had only command bus and external bus and all was fine, but after I added domain and application bus, somethinkg went wrong.
When in tests I'm using ExternalEventBus, to test if external event handled correctly, like this:
$this->eventBus = self::$container->get(PlatformEventBus::class);
$this->eventBus->dispatch(new SomeExternalEvent());
I'm getting error like
Symfony\Component\Messenger\Exception\NoHandlerForMessageException: No handler for message "SomeExternalEvent".
but when I'm running bin/console debug:messenger for both dev and test env, I can see correct mapping between all events and their handlers.
But when I'm debugging it can see that HandlersLocator::getHandlers() for that SomeExternalEvent for some reason returning list of handlers related to command bus instead of external event bus (maybe because command bus set as a default bus)
I know it's a bit hard to explaing and show topic, but I spend already whole day truing to find what is wrong with it. Any help appreciated, thank you!!!
Sorry for any kind of typos, I'm a bit change name of classes for this example and may did some typos

okay, I figured it out, messenger using autowiryng when you injecting MessageBusInterface depends on variable name, so in case of external event bus it should be $externalEventBus and same for other buses. But I do not understand how it worked before, as I mentioned I have command and external bus for a while and it stop working only when I add more buses...

Related

How can one use a defined channel, in monolog handler from a tagged service?

I have the following monolog handler definitions:
# config_prod.yml
app_generic:
type: rotating_file
max_files: 15
path: "%param.app_logging_config.log_generic_file%"
level: info
channels: [app]
app_api:
max_files: 15
path: "%param.app_logging_config.log_api_file%"
level: info
channels: [app]
level: info
app_response:
max_files: 15
path: "%param.app_logging_config.log_response_file%"
channels: [app]
level: info
And in service.yml, my intention is to inject monolog (#logger) with an array of the above defined handlers.
#service.yml
app.app_logger:
class: AppBundle\Classes\AppLogger
arguments: ['#logger': ['#app_generic', '#app_api', '#app_response']]
calls:
- [init, ['%app_logging_config%']
tags:
- { name: monolog.logger, channel: app }
How does one pass arguments to an injected argument?
Update:
Re-reading the description, I was going for this approach, by just tagging on the service definition:
app.logger:
arguments: ['#logger']
tags:
- { name: monolog.logger, channel: app }
channels: ['app']
Or even ( if I understood correctly), adding a channels: ['app'] key and just having this in service argument:
app.logger:
arguments: ['#monolog.logger.app']
I have not been able to use ( or see via dump ) the handlers defined in config_prod.yml. I have placed these at top because of other "fingers_crossed" handlers I thought may interfere.
I woud like to know, why neither of above (documented) approaches seem to work?
Handlers in
monolog:
handlers:
handler1: ...
handler2: ...
are automatically injected in #logger service.
It looks like you need new custom logger. Please read about The DependencyInjection Component
Create dependencies
services:
app_generic:
....
app_api:
....
app_response:
....
Create custom_logger service
custom_logger:
class: Monolog\Logger
arguments: ["my logger", ["#app_generic", "#app_api", "#app_response"]
Inject you custom logger in you service
app.app_logger:
class: AppBundle\Classes\AppLogger
arguments: ['#custom_logger']
calls:
- [init, ['%app_logging_config%']

monolog.logger.db service has been removed

I'm trying to refactor some Symfony 3 code to Symfony 4.
I am getting the following error when attempting to log:
The "monolog.logger.db" service or alias has been removed or inlined
when the container was compiled. You should either make it public, or
stop using the conta iner directly and use dependency injection
instead.
My logging code:
$logger = $container->get('monolog.logger.db');
$logger->info('Import command triggered');
Monolog config:
monolog:
channels: ['db']
handlers:
db:
channels: ['db']
type: service
id: app.monolog.db_handler
app.monolog.db_handler config (Note, I tried public: true here and it had no affect:
app.monolog.db_handler:
class: App\Util\MonologDBHandler
arguments: ['#doctrine.orm.entity_manager']
How can I get this wired up correctly in Symfony 4?
By default all services in Symfony 4 are private (and is the recommended pratice) so you need to "inject" in each Controller each needed service (personally I use a custom CommonControllerServiceClass).
You can also create a public service "alias" to continue accessing the service as you did, but it's not the best pratice to follow (also because I guess you will have many other services to fix).
mylogger.db:
alias: monolog.logger.db
public: true
then you can get the service from the container:
$logger = $container->get('mylogger.db');
Alister's answer is a good start, but you can utilise service arguments binding instead of creating a new service for each logger:
services:
_defaults:
autowire: true
bind:
$databaseLogger: '#monolog.logger.db'
Then just change the argument name in your class:
// in App\Util\MonologDBHandler.php
use Psr\Log\LoggerInterface;
public function __construct(LoggerInterface $databaseLogger = null) {...}
It appears that App\Util\MonologDBHandler may be the only thing that is actively using monolog.logger.db - via a container->get('...') call. (If not, you will want to use this technique to tag the specific sort of logger into more services).
You would be better to allow the framework to build the app.monolog.db_handler service itself, and use the container to help to build it. Normally, to inject a logger service, you will just need to type-hint it:
// in App\Util\MonologDBHandler.php
use Psr\Log\LoggerInterface;
public function __construct(LoggerInterface $logger = null) {...}
However, that will, by default, setup with the default #logger, so you need to add an extra hint in the service definition of the handler that you want a different type of logger:
services:
App\Log\CustomLogger:
arguments: ['#logger']
tags:
- { name: monolog.logger, channel: db }
Now, the logger in CustomLogger should be what you had previously known as monolog.logger.db.
You can also alias a different interface (similar to how the LoggerInterface is aliased to inject '#logger') to the allow for the tagging.

Configuring LiipImagineBundle Symfony to work with Flysystem

I've been trying to configure a CDN (S3) to work with LiipImagineBundle, but keep getting stuck when it is asking for a non-existent service.
What does this mean?
The value of the filesystem_service property must be a service that returns an instance of League\Flysystem\Filesystem.
From here: http://symfony.com/doc/current/bundles/LiipImagineBundle/cache-resolver/flysystem.html
and here: http://symfony.com/doc/current/bundles/LiipImagineBundle/data-loader/flysystem.html
I have tried to create a service that returns an instance of League\Flysystem\Filesystem as follows:
league.flysystem.s3adaptor:
class: League\Flysystem\AwsS3v3\AwsS3Adapter
arguments: ['#acme.s3_client', '%amazon.s3.bucket%', 's3_fs', '#?']
league.flysystem.filesystem:
class: League\Flysystem\Filesystem
arguments: ['#league.flysystem.s3adaptor', '#?']
calls:
- [addPlugin, ['#oneup_flysystem.plugin.list_with']]
I'm not sure if this is along the right lines or not, but I can't get this working.
Any help or advice will be greatly appreciated.
I was on the right lines, I had just misconfigured my LiipImagineBundle slightly.
To return an instance of League\Flysystem\Filesystem you first need to create a service for the adaptor that you want to use, in this case league.flysystem.s3adaptor does this. Then pass that as an argument to league.flysystem.filesystem.
league.flysystem.s3adaptor:
class: League\Flysystem\AwsS3v3\AwsS3Adapter
arguments: ['#acme.s3_client', '%amazon.s3.bucket%']
league.flysystem.filesystem:
class: League\Flysystem\Filesystem
arguments: ['#league.flysystem.s3adaptor']
calls:
- [addPlugin, ['#oneup_flysystem.plugin.list_with']]
LiipImagineBundle config:
liip_imagine:
resolvers:
profile_photos:
flysystem:
filesystem_service: league.flysystem.filesystem
root_url: "https://s3.eu-west-2.amazonaws.com/nameofthebucket/"
cache_prefix: media/cache
visibility: public
loaders:
profile_photos:
flysystem:
filesystem_service: league.flysystem.filesystem
data_loader: profile_photos
cache: profile_photos

Having problems setting up AWS S3/Cloudfront with Symfony and LiipImagineBundle

I'm trying to set up AWS S3/Cloudfront to work with liipimaginebundle in Symfony, but I really have no idea what I'm doing.
So far I have tried the following documented here http://symfony.com/doc/current/bundles/LiipImagineBundle/cache-resolver/aws_s3.html:
Installed aws-sdk-php:
"require": {
"aws/aws-sdk-php": "^3.28",
}
Set up my parameters (with the correct values not this dummy data):
amazon.s3.key: "your-aws-key"
amazon.s3.secret: "your-aws-secret"
amazon.s3.bucket: "your-bucket.example.com"
amazon.s3.region: "your-bucket-region"
Set up a resolver (although I'm not sure what that even means).
"%amazon.s3.cache_bucket%" is in the documentation but the parameter doesn't exist so I used "%amazon.s3.bucket%" instead:
liip_imagine:
cache: profile_photos
resolvers:
profile_photos:
aws_s3:
client_config:
credentials:
key: "%amazon.s3.key%"
secret: "%amazon.s3.secret%"
region: "%amazon.s3.region%"
bucket: "%amazon.s3.bucket%"
get_options:
Scheme: https
put_options:
CacheControl: "max-age=86400"
Added these lines to create the services:
services:
acme.amazon_s3:
class: Aws\S3\S3Client
factory: Aws\S3\S3Client
arguments:
-
credentials: { key: "%amazon.s3.key%", secret: "%amazon.s3.secret%" }
region: "%amazon.s3.region%"
acme.imagine.cache.resolver.amazon_s3:
class: Liip\ImagineBundle\Imagine\Cache\Resolver\AwsS3Resolver
arguments:
- "#acme.amazon_s3"
- "%amazon.s3.bucket%"
tags:
- { name: "liip_imagine.cache.resolver", resolver: "amazon_s3" }
I'm currently getting this error when I run php bin/console server:run:
PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\UndefinedFunctionException: Attempted to call function "S3Client" from namespace "Aws\S3". in /var/www/swing-polls/var/cache/dev/appDevDebugProjectContainer.php:360
I've tried half a dozen other configs/tutorials to no avail. If someone can point me in the right direction I'd be incredibly grateful.
Using the code provided at Simple S3 Symfony Service with a few tweaks, I've been able to get my images to upload to my s3 bucket, but I just don't know how to get liipimaginebundle work with them.
In vendor/liip/imagine-bundle/DependencyInjection/Compiler/ResolversCompilerPass.php you can see the CompilerPass is getting the value from "resolver" attribute of the tag and is using it to create a Reference object. This means the resolver should contain the id of a service.
Try replacing
tags:
- { name: "liip_imagine.cache.resolver", resolver: "amazon_s3" }
with
tags:
- { name: "liip_imagine.cache.resolver", resolver: "acme.amazon_s3" }

Create config collector for exposing config settings accross application

I'm looking for way of exposing config settings across application, where I can in any bundle add what I want. Purpose of this is to expose several things to HTML, parse it and use in JS.
Base part is ConfigService to which can be added ConfigUnit with own logic.
ConfigServiceInterface:
public function getConfig();
public function addUnit(ConfigUnitInterface $unit);
ConfigUnitInterface:
public function getName();
public function getConfig();
best would be to add units in service.yml like this:
services:
service.config:
class: ConfigService
calls:
- [ addUnit, [ "#unit" ] ]
- [ addUnit, [ "#unit2" ] ]
but config service should be declared in some bundle and developer can't change it's declaration.
second thing which comes to my mind was declare units with calling register method which would get ConfigService as parameter.
config_unit:
class: ConfigUnit
arguments: [...]
calls:
- [ register, [ "#service.config" ] ]
but this isn't nice and I must get this service to init calls, so I must get it from container each time.
Is there way to do this automatically? And separate as possible?
Maybe event would be nice for this, but I don't want to allow developer to modify config array directly.
Well I found out how to do it.
documentation helps:
tags, compiler pass
Each my unit has now tag: 'config.unit'
config_unit:
class: ConfigUnit
arguments: [...]
tags:
- { name: config.unit }
these are processed by CompilerPass and injected to ConfigService

Resources