Monolog in Symfony from non-controller Class - symfony

I have the following class:
namespace AppBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
class UserRegistrationListener implements EventSubscriberInterface
{
protected $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInit',
);
}
/**
* take action when registration is initialized
* set the username to a unique id
* #param \FOS\UserBundle\Event\FormEvent $event
*/
public function onRegistrationInit(UserEvent $userevent)
{
$this->logger->info("Log Something");
$user = $userevent->getUser();
$user->setUsername(uniqid());
}
}
and I have been trying for hours to log something with monolog from within it but have had no luck.
I have read much of the documentation and I believe I need to somehow 'Inject' monolog as a service. What I have read however does not seem to be clear to me.
Some details:
#config_dev.yml
monolog:
channels: [chris]
handlers:
mylog:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%_chris.log"
channels: chris
formatter: monolog.my_line_formatter
.
#services.yml
services:
monolog.my_line_formatter:
class: Monolog\Formatter\LineFormatter
arguments: [~, ~, true]
app.user_registration:
class: AppBundle\EventListener\UserRegistrationListener
arguments: [#logger] ## changed to [#monolog.logger.chris] to us custom channel
tags:
- { name: kernel.event_subscriber }
What do I have to do to get Monolog working with my formatter inside this class?
UPDATE:
#griotteau I have done what you have posted in your answer but I still get an error:
CRITICAL - Uncaught PHP Exception Symfony\Component\Debug\Exception\ContextErrorException: "Warning: Missing argument 1 for AppBundle\EventListener\UserRegistrationListener::__construct(), called in ...filepath...\app\cache\dev\appDevDebugProjectContainer.php on line 384 and defined" at ...filepath...\src\AppBundle\EventListener\UserRegistrationListener.php line 18
SOLVED ERROR I already had a service with the same class (not shown in ym question). #griotteau 's answer is correct.

You can pass arguments when you declare your service
In services.yml :
app.user_registration:
class: AppBundle\EventListener\UserRegistrationListener
arguments: [#logger]
tags:
- { name: kernel.event_subscriber }
In your class, add a constructor :
protected $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
So when you want to add a log :
$this->logger->info(...);

Related

How to avoid showing the stack trace in Symfony2 while on production?

When a user without the necessary role tries to access a page that requires a higher token, I rightfully get
AccessDeniedHttpException: Token does not have the required roles
But that is followed by the whole stack trace including the file path. E.g.
'file' => string '/var/www/myApp/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php'
How do I remove the debugging information?
I've made sure I am on prod (app.php) and that
$kernel = new AppKernel('prod', false);
Update
To complete the solution based on Robert's answer, I had to tweak the yml syntax to include a dash
services:
core.exceptlistener:
class: UserBundle\Listener\ExceptionListener
arguments: ['#service_container', '#templating']
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
And then create the twig file at the location specified in onKernelException, ensuring that the file inherited the base twig for the rest of my project
REG: To avoid stack trace you can use Event Listener
Listener Class
<?php
namespace UserBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\Template;
/**
* Created by PhpStorm.
* User: robert
* Date: 20/8/17
* Time: 2:26 PM
*/
class ExceptionListener
{
/**
*
* #var ContainerInterface
*/
private $container;
function __construct($container) {
$this->container = $container;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
// We get the exception object from the received event
$exception = $event->getException();
$response = new Response($this->container->get('templating')->renderResponse('UserBundle:Exception:error403.html.twig',
array('exception'=>$exception->getMessage())));
$event->setResponse($response);
}
}
IN your services.yml
services:
core.exceptlistener:
class: UserBundle\Listener\ExceptionListener
arguments: ['#service_container', '#templating']
tags:
{ name: kernel.event_listener, event: kernel.exception, method: onKernelException }

#mailer and #twig in argument of a service error ServiceCircularReferenceException

I'm trying to put twig like argument of my service but i have always the same error :
ServiceCircularReferenceException in bootstrap.php.cache line 2129
Circular reference detected for service "doctrine.orm.default_entity_manager",path: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> wh.participant_listener -> wh.participant_notification -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_provider.username -> fos_user.user_manager".`
This is my service.yml file
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
arguments: [#mailer, #twig]
wh.participant_listener:
class: WH\TrainingBundle\EventListener\ParticipantListener
arguments: [#wh.participant_notification]
tags:
- { name: doctrine.event_listener, event: postUpdate }
- { name: doctrine.event_listener, event: postPersist }
My PartcicipantListenerFile
namespace WH\TrainingBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use WH\TrainingBundle\Notification\Notification;
class ParticipantListener
{
protected $notification;
public function __construct(Notification $notification)
{
$this->notification = $notification;
}
}
This probleme exist only when i pass #wh.participant_notificationin arguments of my second service
Any body has an idea ?
Thank's a lot
I've find a solution, not pretty, but it works :
First i pass the service container in argument of my service
services:
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
arguments: ['#service_container']
wh.participant_listener:
class: WH\TrainingBundle\EventListener\ParticipantListener
arguments: ['#wh.participant_notification']
tags:
- { name: doctrine.event_listener, event: postPersist }
then in my Notification.php class :
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
private $container;
public function __construct(Container $container) {
$this->container = $container;
}
public function subscribValidation ($participant) {
$templating = $this->container->get('templating');
$mailer = $this->container->get('mailer');
...
I can't create protected var $twig because the probleme persiste.
I repeat, its only with twig service (or template).
Maybe another one find a better solution ...
The circular message, while unclear, should guide you.
Doctrine entity manager loads its listeners,wh.participant_notification among them. Your service requires twig which in turns requires a chain of other things, doctrine entity manager among them. This causes the exception above.
One solution to this issue could be to use setter injection
So you can just define your service as:
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
calls:
- [setMailer, ["#mailer"]]
- [setTemplating, ["#templating"]]
and add to your Notification class the setter methods
class Notification
{
private $mailer;
private $templating;
public function setMailer(\Mailer $mailer)
{
$this->mailer = $mailer;
}
public function setTemplating(\Symfony\Bundle\TwigBundle\TwigEngine $templating)
{
$this->templating= $templating;
}
...your code...

How to use a controller as service with #Route annotation in Symfony 2?

I've a controller which is configuered as a service. I'd like to use #Route annotations to define the route.
When I try to access the route I get:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Galexis\RequestDispatcherBundle\Controller\RequestDispatcherController::__construct() must implement interface Psr\Log\LoggerInterface, none given, called in /Users/ugxnbpluse/development/git/integrationPHP/symfony2/app/cache/dev/classes.php on line 2282 and defined in /Users/ugxnbpluse/development/git/integrationPHP/symfony2/src/Galexis/RequestDispatcherBundle/Controller/RequestDispatcherController.php line 29
From the error message I understand, that symfony doesn't know, that it should not call new for the controller but take it from the DI container.
The symfony docs (last section) tell me to add something like
#Route(service="my_post_controller_service")
but it seams that symfony does not really support the service property:
BadMethodCallException: Unknown property 'service' on annotation 'Symfony\Component\Routing\Annotation\Route'.
Any ideas?
Controller:
class RequestDispatcherController
{
// ...
public function __construct(
LoggerInterface $logger,
RequestDispatcherService $requestDispatcherService)
{
$this->logger = $logger;
$this->requestDispatcherService = $requestDispatcherService;
}
/**
* #Route("requestDispatcher/{applicationName}")
* #return Response
*/
public function dispatch(Request $request)
{
// ...
}
}
routing.yml:
_request_dispatcher:
resource: "#FooRequestDispatcherBundle/Controller/RequestDispatcherController.php"
type: annotation
service.yml:
parameters:
request_dispatcher.class: Foo\RequestDispatcherBundle\Service\RequestDispatcherService
request_dispatcher_controller.class: Foo\RequestDispatcherBundle\Controller\RequestDispatcherController
services:
request_dispatcher_service:
class: "%request_dispatcher.class%"
arguments: ["#foo.integration.application", "#buzz.multi.client" ]
request_dispatcher_controller:
class: "%request_dispatcher_controller.class%"
arguments: ["#logger", "#request_dispatcher_service"]
I think you should use use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; instead of use Symfony\Component\Routing\Annotation\Route;.

can't pass argument to event listener

Using Symfony 2.5.3. I'm trying to send a 'welcome' e-mail when someone has succesfully registered(FOS Userbunde), using an EventListener. The event fired is fos_user.registration.success.
So I added a service:
mycustom_user.registration_success:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: [#mycustom_user.mailer]
tags:
- { name: kernel.event_listener, event: fos_user_registration_success, method: onRegistrationSuccess}
The listener itself:
namespace Mycustom\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Event\FormEvent;
use Mycustom\UserBundle\Mailer\Mailer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RegistrationListener implements EventSubscriberInterface
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$this->mailer->sendWelcomeMessage($user);
$url = $this->router->generate('fos_user_security_login');
$event->setResponse(new RedirectResponse($url));
}
}
The mailer itself contains the rendering of the content of the email, also registered as a service:
mycustom_user.mailer:
class: Mycustom\UserBundle\Mailer\Mailer
arguments: ['#templating']
mycustom_user.mailer is argument for the listener. But somehow I keep getting this error:
Catchable Fatal Error: Argument 1 passed to
Mycustom\UserBundle\EventListener\RegistrationListener::__construct()
must be an instance of Mycustom\UserBundle\Mailer\Mailer, none given,
called in mycustom/app/cache/dev/appDevDebugProjectContainer.php
on line 2214 and defined in mycustom/src/Mycustom/UserBundle/EventListener/RegistrationListener.php line 19
I tried other arguments like #doctrine (and changed the listeners constructor accordingly), but I keep getting the same error. Also the #templating argument to the mailer service doesn't work.
What am I doing wrong here?
So, the problem that I had was that I had two listeners in one class. And the 2nd service definition didn't contain the argument for the constructor. Should be like this:
mycustom_user.registration_initialize:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: ['#mycustom_user.mailer']
tags:
- { name: kernel.event_subscriber, alias: mycustom_user_registration_listener}
mycustom_user.registration_success:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: ['#mycustom_user.mailer']
tags:
- { name: kernel.event_subscriber }

Arguments not being supplied to service

My validator service is not being supplied to validator. I get an error:
"Warning: Missing argument 1 for My\Bundle\Validator\Constraints\MyCustomValidator::__construct()..."
Here is my services.yml
// My\Bundle\Resources\config\services.yml
services:
my.validator.service:
class: My\Bundle\Validator\Constraints\MyCustomValidator
arguments: [ #doctrine.orm.entity_manager ]
Here is my validator class:
// My\Bundle\Validator\Constraints\MyCustomValidator
namespace My\Bundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager;
class MyCustomValidator extends ConstraintValidator
{
private $em;
public function __construct($em)
{
$this->em = $em;
}
public function validate($value, Constraint $constraint)
{
// Do something
}
}
Here's my validation.yml
// My\Bundle\Resources\config\validation.yml
My\Bundle\Entity\Page:
properties:
name:
- NotBlank: ~
- My\Bundle\Validator\Constraints\MyCustom: ~
Here's my Constraint class
namespace My\Bundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class MyCustom extends Constraint
{
public $message = 'Something is wrong with "%string%".';
public function validatedBy()
{
return get_class($this) . 'Validator';
}
}
I'd be very appreciative if someone could help me with this.
I've tried changing the argument name to "#doctrine.orm.default_entity_manager" as well, but no luck.
The problem was my services.yml. Because the service is being used as a validator, I MUST use the validator.constraint_validator tag. This is in the documentation. Whoops!
services:
my.validator.service:
class: My\Bundle\Validator\Constraints\MyCustomValidator
arguments: [ #doctrine.orm.entity_manager ]
tags:
- { name: validator.constraint_validator, alias: my_custom_alias}
I also need to override the Constraint classes validatedBy() method so that it returns the alias above, e.g.:
// My\Bundle\Validator\Constrains\MyCustom.php
public function validatedBy()
{
return 'my_custom_alias';
}
You should be passing the doctrine registry to your service and using that to retrieve an entity manager instance.
Change your constructor to this:
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
Then amend your service configuration to this:
// My\Bundle\Resources\config\services.yml
services:
my.validator.service:
class: My\Bundle\Validator\Constraints\MyCustomValidator
arguments: [ #doctrine ]
Now anytime you need to get an entity manager in MyCustomValidator you can do this
$em = $this->registry->getManager()

Resources