Arguments not being supplied to service - symfony

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()

Related

Cannot autowire service in Symfony 3.4 and FosUserBundle

I try to override REGISTRATION_SUCCESS in FosUserBundle to redirect the admin on user's list after register a new user.
So I have created a new event subscriber :
<?php
namespace AppBundle\EventListener;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class RedirectAfterRegistrationSubscriber implements EventSubscriberInterface
{
private $mailer;
private $tokenGenerator;
private $router;
public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator, UrlGeneratorInterface $router)
{
$this->mailer = $mailer;
$this->tokenGenerator = $tokenGenerator;
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$user->setEnabled(false);
if (null === $user->getConfirmationToken()) {
$user->setConfirmationToken($this->tokenGenerator->generateToken());
}
$this->mailer->sendConfirmationEmailMessage($user);
$url = $this->router->generate('user_index');
$event->setResponse(new RedirectResponse($url));
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess'
];
}
}
and the following service :
app.redirect_after_registration_subscriber:
class: AppBundle\EventListener\RedirectAfterRegistrationSubscriber
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
I don't understand why this error appears :
Cannot autowire service "AppBundle\EventListener\RedirectAfterRegistrationSubscriber":
argument "$mailer" of method "__construct()" references interface
"FOS\UserBundle\Mailer\MailerInterface" but no such service exists. You should maybe alias
this interface to one of these existing services: "fos_user.mailer.default",
"fos_user.mailer.twig_swift", "fos_user.mailer.noop".
I suppose you are using autodiscovering of services. Something like:
# services.yaml
AppBundle\:
resource: '../src/'
...
So in addition to the #app.redirect_after_registration_subscriber that you define, Symfony defines another service with id #AppBundle\EventListener\RedirectAfterRegistrationSubscriber. Both point to AppBundle\EventListener\RedirectAfterRegistrationSubscriber class. Yet you configured the mailer parameter only for the first one.
The solution:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
With autowiring and autoconfigure you can even sypmlify to:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments:
$mailer: '#fos_user.mailer'

Monolog in Symfony from non-controller Class

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(...);

How to have a global variable coming from db in symfony template?

How can I have a global variable in symfony template?
I did read this
but I prefer to fetch parameter from database, I think this service will be loaded on startup before it can fetch anything from db. Is it possible to do a trick to do so?
EDIT: Update in 2019 with Symfony 3.4+ syntax.
Create a Twig extension where you inject the Entity Manager:
Fuz/AppBundle/Twig/Extension/DatabaseGlobalsExtension.php
<?php
namespace Fuz\AppBundle\Twig\Extension;
use Doctrine\ORM\EntityManager;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
class DatabaseGlobalsExtension extends AbstractExtension implements GlobalsInterface
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function getGlobals()
{
return [
'myVariable' => $this->em->getRepository(FuzAppBundle\Entity::class)->getSomeData(),
];
}
}
Register your extension in your Fuz/AppBundle/Resources/config/services.yml:
services:
_defaults:
autowire: true
autoconfigure: true
Fuz\AppBundle\Twig\Extension\DatabaseGlobalsExtension: ~
Now you can do the requests you want using the entity manager.
Don't forget to replace paths and namespaces to match with your application.
As of this day, the class signature has changed. You must implement \ Twig_Extension_GlobalsInterface, without it, your globals won't show up.
class MyTwigExtension extends \Twig_Extension implements Twig_Extension_GlobalsInterface
{ }
Bye!
you can register a twig extension
services:
twig_extension:
class: Acme\DemoBundle\Extension\TwigExtension
arguments: [#doctrine]
tags:
- { name: twig.extension }
And then in the TwigExtension you can do as follows:
class TwigExtension extends \Twig_Extension
{
public function getGlobals() {
return array(
// your key => values to make global
);
}
}
So you could get a value from the database in this TwigExtension and pass it to the template with the getGlobals() function
Stay away from global variables.
Instead make a custom twig extension then inject the database connection as a parameter.
Something like:
services:
acme.twig.acme_extension:
class: Acme\DemoBundle\Twig\AcmeExtension
arguments: [#doctrine.dbal.default_connection]
tags:
- { name: twig.extension }
Details:
http://symfony.com/doc/current/cookbook/templating/twig_extension.html

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 }

Custom Constraint Validator with Database Connection Dependency Symfony2

I'm adding a custom validation query to a Symfony2 project.
The docs lack a complete example, and I'm not sure how to actually inject the database connection into the Validator Class. I've created the service in my config, added the validatedBy alias method in my Constraint class, and set up this in my Validator Class:
use Doctrine\DBAL\Connection;
class ZipDatabaseValidator extends ConstraintValidator
{
/**
*
* #var Connection
*/
private $connection;
public function __construct(Connection $dbalConnection) {
$this->connection = $dbalConnection;
}
public function validate($zipcode, Constraint $constraint)
{
$sql = 'SELECT * FROM zip_table WHERE zip_code = ?';
$stmt = $this->connection->prepare($sql);
...
Here's my service config:
validator.node.zip_in_database:
class: Acme\Bundle\Validator\Constraints\ZipDatabaseValidator
arguments: [#database_connection]
tags:
- { name: validator.constraint_validator, alias: zip_in_database }
I keep getting errors, in this case:
Catchable Fatal Error: Argument 1 passed to
Acme\Bundle\Validator\Constraints\ZipDatabaseValidator::__construct()
must be an instance of Doctrine\DBAL\Connection, none given,
How the heck to I set this up as a service or otherwise inject the database connection?
validator.node.zip_in_database:
class: Acme\Bundle\Validator\Constraint\ZipDatabaseValidator
arguments: [#database_connection]
tags:
- { name: validator.constraint_validator, alias: zip_in_database }
You must pass doctrine as an argument to your Service.
Edit
Make sure the alias is the same as the validatedBy method returns!
in your case:
//Acme\Bundle\Validator\Constraint\ZipDatabase class
public function validatedBy()
{
return 'zip_in_database';
}

Resources