I'm beginner in Symfony (6.1) and sometimes I need to get the current User in my controllers.
The way I use for the moment is :
$user = $userRepository->find($this->getUser()->getId());
But they are a better way ?
Because $this->getUser() give me the UserInterface and I need the User entity. screenshot example
Thanks to read me
Using $this->getUser() will get you the current user (that implements UserInterface). The getUser() method lives in AbstractController.php that is part of the Symfony FrameworkBundle. You could extend this controller if you really want to change the getUser() method to not use the UserInterface, but I think it would be better to simply change the typehint of the function (setUser) you call (in your screenshot -please write this out next time-) to use the UserInterface. Something like this:
public function setUser(UserInterface $user)
{
//....
}
Edit:
After consideration, thanks to Cerad's valid point below, it is probably best to use setUser(User $user) given that you might use another class that implements UserInterface. Then either use var annations to tell your IDE which class you use, like so:
/** #var User $user */
$user = $this->getUser();
Or extend AbstractController so that it has a getUser method with a User return type like so:
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class BaseController extends AbstractController
{
protected function getUser(): ?User
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return null;
}
return $token->getUser();
}
}
or maybe even better, like this:
/**
* #return User|null
*/
protected function getUser(): ?UserInterface
{
return parent::getUser();
}
I'm a bit puzzled by some of the other answers so it might be that I am the one who is confused. The basic problem is that getUser is typehinted to return an UserInterface but the actual User object is needed. My solution is to explicitly tell the IDE what getUser is actually returning:
use App\Entity\User; # or whetever the application defined user is
class MyController extends AbstractController
{
public function someMethod()
{
/** #var User */
$user = $this->getUser();
And everyone, especially the IDE, is happy.
Once again you need to do what is basically a typecast because the security system really only cares about the UserInterface and does not worry about anything else the user might have. Hence we have:
class AbstractController {
protected function getUser(): ?UserInterface {
A bit off-topic but I am looking forward to the day when this bit of annotation is no longer needed and replace with something like:
User $user = $this->getUser();
You have many ways to get your current user, please try this one:
$creditCard= new creditCard();
$creditCard->setUser($this->getUser());
Or this one:
$creditCard= $this->get('security.token_storage')->getToken()->getUser();
Related
I'm trying to validate my entity via static callback.
I was able to make it work following the Symfony guide but something isn't clear to me.
public static function validate($object, ExecutionContextInterface $context, $payload)
{
// somehow you have an array of "fake names"
$fakeNames = array(/* ... */);
// check if the name is actually a fake name
if (in_array($object->getFirstName(), $fakeNames)) {
$context->buildViolation('This name sounds totally fake!')
->atPath('firstName')
->addViolation()
;
}
}
It works fine when I populate my $fakeNames array but what if I want to make it "dynamic"? Let's say I want to pick that array from the parameters or from the database or wherever.
How am I supposed to pass stuff (eg. the container or entityManager) to this class from the moment that the constructor doesn't work and it has to be necessarily static?
Of course my approach may be completely wrong but I'm just using the symfony example and few other similar issues found on the internet that I'm trying to adapt to my case.
You can create a Constraint and Validator and register it as service so you can inject entityManager or anything you need, you can read more here:
https://symfony.com/doc/2.8/validation/custom_constraint.html
or if you are on symfony 3.3 it is already a service and you can just typehint it in your constructor:
https://symfony.com/doc/current/validation/custom_constraint.html
This is the solution I was able to find in the end.
It works smoothly and I hope it may be useful for someone else.
I've set the constraint on my validation.yml
User\UserBundle\Entity\Group:
constraints:
- User\UserBundle\Validator\Constraints\Roles\RolesConstraint: ~
Here is my RolesConstraint class
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\Validator\Constraint;
class RolesConstraint extends Constraint
{
/** #var string $message */
public $message = 'The role "{{ role }}" is not recognised.';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
and here is my RolesConstraintValidator class
<?php
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class RolesConstraintValidator extends ConstraintValidator
{
/** #var ContainerInterface */
private $containerInterface;
/**
* #param ContainerInterface $containerInterface
*/
public function __construct(ContainerInterface $containerInterface)
{
$this->containerInterface = $containerInterface;
}
/**
* #param \User\UserBundle\Entity\Group $object
* #param Constraint $constraint
*/
public function validate($object, Constraint $constraint)
{
if (!in_array($object->getRole(), $this->containerInterface->getParameter('roles'))) {
$this->context
->buildViolation($constraint->message)
->setParameter('{{ role }}', $object->getRole())
->addViolation();
}
}
}
Essentially, I set up a constraint which, every time a new user user is registered along with the role, that role must be among those set in the parameters. If not, it builds a violation.
All of my query in Entity Repository needs to be filtered by user.
Now I want to know how can I access the currently logged in user in Entity Repository directly.
What I did today is to get the currently logged in user in my controller, through the use of $this->getUser() and then pass it to Entity Repository and this is not efficient.
You need to inject security.token_storage service into another one to get the current user, but as of Repository classes belong to Doctrine project, not Symfony, it is not recommended to do this.. May be there is a way to achieve it by creating custom entityManager class as described here, but I don't think it would a good solution..
Instead of customizing an entityManager better create a service which calls repository classes' methods, inject desired services into it.. Let Repository classes do their job.
Implementation would be something like this:
RepositoryClass:
class MyRepository extends EntityRepository
{
public function fetchSomeDataByUser(UserInterface $user)
{
// query
}
}
Service:
class MyService
{
private $tokenStorage;
public function _construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
// other services
}
public function getSomeDataByUser()
{
$user = $this->tokenStorage->getToken()->getUser();
return $this->entityManager->getRepository(MyREPOSITORY)->fetchSomeDataByUser($user);
}
}
Usage:
public function someAction()
{
$dataByUser = $this->get(MYSERVICE)->getSomeDataByUser();
}
If you use JMSDiExtraBundle it can be done by adding setter injection:
use Doctrine\ORM\EntityRepository;
use JMS\DiExtraBundle\Annotation as DI;
class YourRepository extends EntityRepository
{
/** #var User current user entity */
protected $user;
/**
* #DI\InjectParams({
* "token_storage" = #DI\Inject("security.token_storage")
* })
*/
public function setSimplaManager(TokenStorageInterface $tokenStorage)
{
$token = $tokenStorage->getToken();
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}
My point is to call a generic function in all my controllers in some Bundle (let's say AdminBundle). I got a login listener in whitch I set a session that contain true or false and I need to check this session before every method of my AdminBundle.
So I tried to make a __construct() function in my AdminBundle controllers, but it appears that I can't access to a service from this method (because the container is not yet loaded so I can't access $this).
The best practice should be to use a listener to call this service before very method of those controllers but I can't figure out whitch listener I need to use (cannot find a clue on google...).
Hope in clear enough, don't hesitate to ask questions if you don't understand my point !
Thanks in advance for any solution/idea (if you think that I'm not using the correct way to do it please explain my your point of view !)
After the afternoon on this issue i finally get a solution thanks to mahok.
For those whitch have the same issue here's my controller listener :
<?php
namespace Site\MyBundle\Listener;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ControllerListener
{
protected $container;
protected $router;
public function __construct(ContainerInterface $container, Router $router)
{
$this->router = $router;
$this->container = $container;
}
public function onKernelController(FilterControllerEvent $event)
{
if (HttpKernel::MASTER_REQUEST == $event->getRequestType())
{
$controller = $event->getController();
$controller = $controller[0];
$new = new \ReflectionObject($controller);
if($new->getNamespaceName() == 'Site\MyBundle\Controller')
{
$test = $this->container->get('myservice')->test();
if(empty($test) || !$test)
{
$index = $this->router->generate('index');
$event->setController(function() use($index) {
return new RedirectResponse($index);
});
}
}
}
}
}
So basically it compare the namespace of your current controller's action with another and if true i check some variable to know if the user can be here or not.
Thanks again mahok you show me the way !
I'm trying to create a new User Admin from a fixture. I'm using FOSUserBundle and Symfony2.
$userManager = $this->container->get('fos_user.user_manager');
//$userAdmin = $userManager->createUser();
$userAdmin = new UserAdmin();
$userAdmin->setUsername('francis');
$userAdmin->setEmail('francis#francis.com');
$userAdmin->setEnabled(true);
$userAdmin->setRoles(array('ROLE_ADMIN'));
$userManager->updateUser($userAdmin, true);
I'm always getting this error:
[ErrorException]
Notice: Undefined property:
INCES\ComedorBundle\DataFixtures\ORM\LoadUserAdminData::$container in
/public_html/Symfony/src/INCES/ComedorBundle/DataFixtures/ORM/LoadUserAdminData.php line 16
This worked for me (i'm also using FOSUserBundle):
// Change the namespace!
namespace Acme\DemoBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
//.. $container declaration & setter
public function load(ObjectManager $manager)
{
// Get our userManager, you must implement `ContainerAwareInterface`
$userManager = $this->container->get('fos_user.user_manager');
// Create our user and set details
$user = $userManager->createUser();
$user->setUsername('username');
$user->setEmail('email#domain.com');
$user->setPlainPassword('password');
//$user->setPassword('3NCRYPT3D-V3R51ON');
$user->setEnabled(true);
$user->setRoles(array('ROLE_ADMIN'));
// Update the user
$userManager->updateUser($user, true);
}
}
Hope this helps someone! :)
Follow this section of the documentation.
The Error is because the $container is currently undefined. To fix this, add the ContainerAwareInterface to your class definition.
class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
...
}
This will not completely get you what you want though, since you are creating the user without the UserManager. Instead you should be using the line you have commented out.
It seems to me that you don't need the UserAdmin class. The admin users should be a subset of the User distinguished only by the roles that they have.
You should use the UserManager to create a User(not UserAdmin) and set the roles.
If you need to keep an index of all admin users, a MySQL VIEW could accomplish this, or you could create your own custom "cache" table and use Doctrine Listeners to update it when needed.
This question is fairly old, so I am guessing you found the answer or at least a workaround.
Would you please provide that? It is ok to answer your own questions.
This is the updated version for SF3+
This answer is based on Anil and Mun Mun Das answers.
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use FOS\UserBundle\Model\UserManagerInterface;
class AdminFixtures extends Fixture
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function load(ObjectManager $manager)
{
$user = $this->userManager->createUser();
$user->setUsername('username');
$user->setEmail('email#domain.com');
$user->setPlainPassword('password');
$user->setEnabled(true);
$user->setRoles(array('ROLE_ADMIN'));
$this->userManager->updateUser($user);
}
}
This is what I did using Symfony 4, SonataAdmin and FosUserBundle
namespace App\DataFixtures;
use App\Application\Sonata\UserBundle\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class UserFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('yourusername');
$user->setEmail('youremail#email.com');
$user->setEnabled(true);
$user->setPlainPassword('yourpassword');
$user->setRoles(array('ROLE_SUPER_ADMIN'));
$manager->persist($user);
$manager->flush();
}
}
I´ve created my own user Bundle, extending FOSUserBundle.
In my UserBundle, I have a memberID field, with its setters and getters.
I can already find users by memberID using EntityManager, and then I could find the user through UserManager matching the username/email/... obtained with that EntityManager query, but...
Is there a way to findUserByMemberID using UserManager?
Thank you.
Thank you for your replies. It seems it´s easier than all this stuff.
OwnUserBundle:
/**
* #ORM\Column(type="string")
*
*/
protected $memberID;
public function getMemberID()
{
return $this->memberID;
}
public function setMemberID($memberID)
{
$this->memberID = $memberID;
}
You can query FOSUserBundle:
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('memberID' => '123'));
Then, using method findUserBy(array(*OwnUserBundle_field* => *search_parameter_value*)) you´ll get the user/s instance.
Every query that is "not standard" one has to be written into a repository class
You have also to relate this class with one that represent you data model.
Suppose that your entity is called User, you have to do something like this
/**
* VendorName\UserBundle\Entity\User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="VendorName\UserBundle\Repository\UserRepository")
*/
class User implements AdvancedUserInterface
{
[...]
This says that every "custom" query for that entity will be fit into that repository class.
Now you have to create the repository
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function findUserByMemberID($id)
{
/* your logic here */
}
[...]
and you can use that in the following way
$userRepo = $this->em->getRepository('VendorUserBundle:User');
$userRepo->findUserByMemberID();
You could extend the UserManager from the FOSUserBundle in your bundle and write your own method. And you could follow these instructions http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes