Can somebody show me how I would inject validator into a regular class using dependency injection.
In my controller I have :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Form;
class FormController extends Controller {
public function indexAction()
{
$form = new Form();
$email = $request->request->get('email');
$valid = $form->isValid($email);
}
}
and I want to use this custom class - but I need it got have access to validator.
class Form {
public function isValid($value)
{
// This is where I fail
$validator = $this->get('validator');
... etc
}
}
To do this, your custom class will need to be defined as a service, and you'll access it from the controller using $form = $this->get('your.form.service'); instead of instantiating it directly.
When defining your service, make sure to inject the validator service:
your.form.service:
class: Path\To\Your\Form
arguments: [#validator]
Then you'll need to handle this in the construct method of your Form service:
/**
* #var \Symfony\Component\Validator\Validator
*/
protected $validator;
function __construct(\Symfony\Component\Validator\Validator $validator)
{
$this->validator = $validator;
}
From Symfony2.5 on
Validator is called RecursiveValidator so, for injection
use Symfony\Component\Validator\Validator\RecursiveValidator;
function __construct(RecursiveValidator $validator)
{
$this->validator = $validator;
}
In Symfony 4+, if you use the default configuration (with auto wiring enabled), the easiest way is to inject a ValidatorInterface.
For instance:
<?php
use Symfony\Component\Validator\Validator\ValidatorInterface;
class MySuperClass
{
private $validator;
public function __construct(
ValidatorInterface $validator
) {
$this->validator = $validator;
}
Your Form class can inherit from ContainerAware (Symfony\Component\DependencyInjection\ContainerAware). Then you will have access to the container and you will be able to get the validator service like this:
$validator = $this->container->get('validator');
Related
I want to split my codebase to simple one purpose specific classes like:
class AddKeyword
{
/**
* #var KeywordRepository
*/
private $keywordRepository;
public function __construct(KeywordRepository $keywordRepository)
{
$this->keywordRepository = $keywordRepository;
}
public function __invoke(string $name): Keyword
{
$entity = $this->keywordRepository->findOneByName($name);
if ($entity)
return $entity;
$entity = Keyword::create(KeywordId::create(), $name);
$this->keywordRepository->save($entity);
return $entity;
}
}
But for using that class I have to resolve DI. How to do it?
Thank you in advance.
Not sure about what you want to achieve but if you want add/get keyword everywhere in your code base you have 2 choices:
Use autowiring
Declare your class as service and get it from the container.
Symfony encourage DI by autowiring.
namespace App\Controller;
use App\AddKeyword;
class DefaultController
{
public function __construct(Addkeyword $keyword) {
$keyword('keyword');
}
}
In Symfony 2.8/3.0, with our fancy new security components, how do I get the currently logged User (i.e. FOSUser) object in a service without injecting the whole container?
Is it even possible in a non-hacky way?
PS: Let's not consider the "pass it to the service function as a parameter" for being trivially obvious. Also, dirty.
Inject security.token_storage service into your service, and then use:
$this->token_storage->getToken()->getUser();
as described here: http://symfony.com/doc/current/book/security.html#retrieving-the-user-object and here: http://symfony.com/doc/current/book/service_container.html#referencing-injecting-services
Works with Symfony 3.4, 4.x, 5.x & above. The Security utility class was introduced in Symfony 3.4.
use Symfony\Component\Security\Core\Security;
public function indexAction(Security $security)
{
$user = $security->getUser();
}
https://symfony.com/doc/3.4/security.html#always-check-if-the-user-is-logged-in
Using constructor dependency injection, you can do it this way:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class A
{
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
}
public function foo()
{
dump($this->user);
}
}
In symfo 4 :
use Symfony\Component\Security\Core\Security;
class ExampleService
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function someMethod()
{
$user = $this->security->getUser();
}
}
See doc : https://symfony.com/doc/current/security.html#retrieving-the-user-object
From Symfony 3.3, from a Controller only, according this blog post: https://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers
It's easy as:
use Symfony\Component\Security\Core\User\UserInterface
public function indexAction(UserInterface $user)
{...}
With Symfony 5.2+ and PHP 8.0+ you can also get the logged user using the #[CurrentUser] attribute
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
class FooController extends AbstractController
{
public function index(#[CurrentUser] ?User $user)
{
// ...
}
}
Blog post: https://symfony.com/blog/new-in-symfony-5-2-controller-argument-attributes
Documentation: https://symfony.com/doc/current/security.html
Symfony does this in Symfony\Bundle\FrameworkBundle\ControllerControllerTrait
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
return $user;
}
So if you simply inject and replace security.token_storage, you're good to go.
if you class extend of Controller
$this->get('security.context')->getToken()->getUser();
Or, if you has access to container element..
$container = $this->configurationPool->getContainer();
$user = $container->get('security.context')->getToken()->getUser();
http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
Version : Symfony 2.2
I'm trying to add a default role when a user register on my website. I use FOSUserBundle and i see that when a user register the role field is empty in a database.
I begin with this huge bundle and it's not very easy to understand. So i read all the documentation and i'm not sur what to do.
For now, i create an Event to add this role dynamically, but it doesn't work (i have no error but my database is still empty) I'm not even sur this is the good way to do that ?
My Event :
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class AddDefaultRoleListener implements EventSubscriberInterface {
private $container;
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onAddDefaultRoleSuccess',
);
}
public function onAddDefaultRoleSuccess(FormEvent $event)
{
$doctrine = $this->container->get('doctrine');
$em = $doctrine->getManager();
$user = $event->getForm()->getData();
$user->addRole('ROLE_USER');
//$user->setRoles(array('ROLE_USER'));
$em->persist($user);
}
}
As you see i create a simple event which listen on REGISTRATION_SUCCESS, but nothing seems to work. It's my first try with Events and services. So if someone has an advice, i'll take it :)
The recommended way to do it as indicated by a main contributor to the FOSUserBundle (in the comment here linked) is to register an Event Listener on the REGISTRATION_SUCCESS event and use the $event->getForm()->getData() to access the user and modify it.
Following those guidelines, I created the following listener (which works!):
<?php
// src/Acme/DemoBundle/EventListener/RegistrationListener.php
namespace Acme\DemoBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listener responsible for adding the default user role at registration
*/
class RegistrationListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$rolesArr = array('ROLE_USER');
/** #var $user \FOS\UserBundle\Model\UserInterface */
$user = $event->getForm()->getData();
$user->setRoles($rolesArr);
}
}
Also, the service needs to be registered as follows:
// src/Acme/DemoBundle/Resources/config/services.yml
services:
demo_user.registration_listener:
class: Acme\DemoBundle\EventListener\RegistrationListener
arguments: []
tags:
- { name: kernel.event_subscriber }
Notice that adding a default role in the User class __construct() may have some issues as indicated in this other answer.
What i have done is override the entity constructor:
Here a piece of my Entity/User.php
public function __construct()
{
parent::__construct();
// your own logic
$this->roles = array('ROLE_USER');
}
This is the lazy way. If you want the right and better way see the #RayOnAir answer
I think #RayOnAir solution is right way of doing this. But it will not work due to FOS default role handling
to make possible to persist default role in database one need to override User::setRoles() method (add it to your User entity):
/**
* Overriding Fos User class due to impossible to set default role ROLE_USER
* #see User at line 138
* #link https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Model/User.php#L138
* {#inheritdoc}
*/
public function addRole($role)
{
$role = strtoupper($role);
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}
Tested under:
Symfony version 2.3.6,
FOSUserBundle 2.0.x-dev
You can add an Event Subscriber to a Form Class and use the form event "formEvents::POST_SUBMIT"
<?php
//src/yourNS/UserBundle/Form/Type/RegistrationFormType.php
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use yourNS\UserBundle\Form\EventListener\AddRoleFieldSubscriber;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
// add your custom field
$builder->add('firstName')
->add('lastName')
->add('address')
//...
//...
->add('phone');
$builder->addEventSubscriber(new AddRoleFieldSubscriber());
}
public function getName()
{
return 'yourNS_user_registration';
}
}
Now the logic for adding the role field resides in it own subscriber class
<?php
//src/yourNS/UserBundle/Form/EventListener/AddRoleFieldSubscriber.php
namespace yourNS\UserBundle\Form\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AddRoleFieldSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(FormEvents::POST_SUBMIT => 'setRole');
}
public function setRole(FormEvent $event)
{
$aRoles = array('ROLE_USER');
/** #var $user \FOS\UserBundle\Model\UserInterface */
$user = $event->getForm()->getData();
$user->setRoles($aRoles);
}
}
Ok now it's working with that :
public function onAddDefaultRoleSuccess(FilterUserResponseEvent $event)
{
$doctrine = $this->container->get('doctrine');
$em = $doctrine->getManager();
$user = $event->getUser();
$user->addRole('ROLE_BLOGGER');
$em->persist($user);
$em->flush();
}
I change my listener and know use REGISTRATION_COMPLETED. If someone has a better idea to do that, don't hesitate :)
public function __construct()
{
parent::__construct();
$this->setRoles(["ROLE_WHATEVER"]);
}
I followed the tutorial of Fabien Potiencier, about how to create your own Framework on top of the Symfony Components. Now i need a way. And I want to inject the Dependency Container to all my Controllers, without defining every single Controller as a Service.
In the orginal Symfony2 Framework all Controllers extends the Controller Class located in Symfony\Bundle\FrameworkBundle\Controller\Controller.php:
namespace Symfony\Bundle\FrameworkBundle\Controller;
class Controller extends ContainerAware
{
// ...
}
The Controller Class extends the ControllerAware Class, so you can do something like this in your Controller:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyController extends Controller
{
public function someAction()
{
$this->container->get('dependencie_xyz);
}
}
So my question is: How can I accomplish the same in my Framework?
It took me a while, but i finally figured out how the Symfony2 Framework does it.
In the SymfonyFrameworkBundle is a custom ControllerResolver, which call the setContainer Method on the resolved controller. The controller has to be a instance of the ContainerAwareInterface.
Simplified version:
class ContainerAwareControllerResolver extends ControllerResolver
{
private $container;
public __construct(ContainerInterface $container)
{
$this->container = $container;
parent::__construct();
}
public function getController(Request $request)
{
$controller = parent::getController($request);
if($controller instanceof ContainerAware ){
$controller->setContainer($this->container);
}
}
}
Source:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php
It is too simply. The next code will help you
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Symfony\Component\DependencyInjection\ContainerAware as ContainerAware;
class TestService extends ContainerAware
{
public function __construct(Container $container) {
// in your example from official doc 'dependencie_xyz' is a name of service
$this->setContainer($container); // call parent setContainer() method, for identifying container variable, from now you can access to ServiceContainer using $this->container variable
$test_param = $this->container->getParameter('test_param'); // get test_param from config.yml
}
}
in service.yml
write smthing like this
services:
test_service:
class: Symfony\Bundle\FrameworkBundle\TestService
arguments: ['#service_container']
and post service container as argument
If you are not implementing any interface on controller you can add the this way and it will work. This is a small modification to c4pone implementation.
/**
* Description of ContainerAwareControllerResolver
*
* #author sbc
*/
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
class ContainerAwareControllerResolver extends ControllerResolver {
private $container;
public function __construct(LoggerInterface $logger = null, ContainerInterface $container = null) {
parent::__construct($logger);
$this->container = $container;
}
protected function instantiateController($class) {
$new_class = new $class();
$new_class->setContainer($this->container);
return $new_class;
}
The Controller Class extends the ControllerAware Class, so you can do something like this in your Controller:
Well, this is not true. If we take a look at the signature of the ContainerAware class, we see that this added a setContainer method so we can set the container. Symfony2 has created the Controller::get method to make some live easier.
We can see how they do it in the source code:
/**
* Gets a service by id.
*
* #param string $id The service id
*
* #return object The service
*/
public function get($id)
{
return $this->container->get($id);
}
You can put this in your own Controller class and let all your controllers extend that controller class.
How can I define a constructor in Symfony2 controller. I want to get the the logged in user data available in all the methods of my controller, Currently I do something like this in every action of my controller to get the logged in user.
$em = $this->getDoctrine()->getEntityManager("pp_userdata");
$user = $this->get("security.context")->getToken()->getUser();
I want to do it once in a constructor and make this logged in user available on all my actions
For a general solution for executing code before every controller action you can attach an event listener to the kernel.controller event like so:
<service id="your_app.listener.before_controller" class="App\CoreBundle\EventListener\BeforeControllerListener" scope="request">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/>
<argument type="service" id="security.context"/>
</service>
Then in your BeforeControllerListener you will check the controller to see if it implements an interface, if it does, you will call a method from the interface and pass in the security context.
<?php
namespace App\CoreBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Security\Core\SecurityContextInterface;
use App\CoreBundle\Model\InitializableControllerInterface;
/**
* #author Matt Drollette <matt#drollette.com>
*/
class BeforeControllerListener
{
protected $security_context;
public function __construct(SecurityContextInterface $security_context)
{
$this->security_context = $security_context;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
// not a object but a different kind of callable. Do nothing
return;
}
$controllerObject = $controller[0];
// skip initializing for exceptions
if ($controllerObject instanceof ExceptionController) {
return;
}
if ($controllerObject instanceof InitializableControllerInterface) {
// this method is the one that is part of the interface.
$controllerObject->initialize($event->getRequest(), $this->security_context);
}
}
}
Then, any controllers that you want to have the user always available you will just implement that interface and set the user like so:
use App\CoreBundle\Model\InitializableControllerInterface;
class DefaultController implements InitializableControllerInterface
{
/**
* Current user.
*
* #var User
*/
private $user;
/**
* {#inheritdoc}
*/
public function initialize(Request $request, SecurityContextInterface $security_context)
{
$this->user = $security_context->getToken()->getUser();
}
// ....
}
The interface is nothing more than
namespace App\CoreBundle\Model;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;
interface InitializableControllerInterface
{
public function initialize(Request $request, SecurityContextInterface $security_context);
}
I'm runnig a bit late, but in a controller you can just access the user:
$this->getUser();
Should be working since 2.1
My approach to this was:
Make an empty Interface InitializableControllerInterface
Make event Listener for
namespace ACMEBundle\Event;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class ControllerConstructor
{
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
// not a object but a different kind of callable. Do nothing
return;
}
$controllerObject = $controller[0];
if ($controllerObject instanceof InitializableControllerInterface) {
$controllerObject->__init($event->getRequest());
}
}
}
In your controller add:
class ProfileController extends Controller implements
InitializableControllerInterface
{
public function __init()
{
$this->user = $security_context->getToken()->getUser();
}
And you will be able to get the $this->user in each action.
Regards