Custom Constraint Validator with Database Connection Dependency Symfony2 - symfony

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';
}

Related

Symfony service arguments ignored

I have an entity listener declared with annotation
...
* #ORM\EntityListeners({ "EnvBundle\Listener\UserListener" })
*/
class User implements UserInterface, \Serializable
{
...
and since that listener should have a parameter injected, say a string, I declared it as a service in the service.yml file
services:
env.listener.user:
class: EnvBundle\Listener\UserListener
arguments: ['humpty dumpty']
Listener is roughly like this
<?php
namespace EnvBundle\Listener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use EnvBundle\Entity\User;
class UserListener
{
public function __construct(string $s)
{
dump('no good: ' . $s);
}
public function prePersist(User $entity, LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
dump($entity);
// die;
}
}
Result is that the parameter injected via the service is ignored and I get the following error
Type error: Argument 1 passed to
EnvBundle\Listener\UserListener::__construct() must be of the type
string, none given
Once I declare the constructor without parameter it gets executed. I can't find out what I'm missing to get the parameters injected into the entity listener.
Edit: by checking debug:container the service does exist
Any suggestion?

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

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

How do you access Doctrine DBAL in a Symfony2 service class?

I'm learning Symfony2 (and OOP) and want to create a service that's available throughout my app. This service takes a value foo, checks it against a database table, and returns a value bar.
I have a little class
namespace Acme\TestBundle\Toolbox;
class StringToolbox
{
public function lookupSomething($foo)
{
$conn = $this->get('database_connection');
$sql = "SELECT bar FROM bar_list WHERE foo = :foo";
$stmt = $conn->prepare($sql);
$stmt->bindValue("foo", $foo);
$stmt->execute();
return $bar;
}
}
My settings are:
services:
toolbox:
class: Acme\TestBundle\Toolbox
arguments: [#database_connection]
But it throws an error saying that the get() method is undefined. I'm stuck-- how can I use DBAL in the service? Thanks!
First off you should add a constructor to your class and pass in the #doctrine.dbal.%connection_name%_connection service
namespace Acme\TestBundle\Toolbox;
use Doctrine\DBAL\Connection;
class StringToolbox
{
/**
*
* #var Connection
*/
private $connection;
public function __construct(Connection $dbalConnection) {
$this->connection = $dbalConnection;
}
public function lookupSomething($foo)
{
$sql = "SELECT bar FROM bar_list WHERE foo = :foo";
$stmt = $this->connection->prepare($sql);
$stmt->bindValue("foo", $foo);
$stmt->execute();
return $bar;
}
}
Your service configuration should now look like this:
parameters:
my_service_connection: default
services:
toolbox:
class: Acme\TestBundle\Toolbox\StringToolbox
arguments: [#doctrine.dbal.%my_service_connection%_connection]
What you are saying with this configuration is "make me a service named toolbox that will receive the doctrine.dbal.default_connection service as the first constructor argument"
There are other injection methods besides Constructor injection and you should read the http://symfony.com/doc/current/book/service_container.html documentation to get a grasp of all possibilities (Setter injection, Factory injection, etc) and to better understand how Dependency Injection works
#doctrine.dbal.connection not working, As Igor says, #doctrine.dbal.connection is an abstract, use #doctrine.dbal.default_connection if you only have one db connection, or #doctrine.dbal.%connection_name%_connection where the %connection_name% placeholder the name of the connection that you want to inject.
Your service configuration should now look like this:
services:
toolbox:
class: Acme\TestBundle\Toolbox\StringToolbox
arguments: [#doctrine.dbal.default_connection]

Resources