Symfony FOSUserBundle - Create custom repository to persist users - symfony

I am using FOSUserBundle to be able to manage users in a symfony2 project.
Since using container is not recommended my question is how can I extend the FOSUserBundle to be able to create a custom save method like this, for example:
class UserRepository extends EntityRepository
{
public function registration(array $data)
{
// example only
$userManager = $this->container->get('fos_user.user_manager');
//$em = $this->container->get('doctrine')->getEntityManager();
//$userUtils = $this->container->get('fos_user.util.token_generator');
$user = $userManager->createUser();
$user->setFirstName($data['first_name']);
$user->setLastName($data['last_name']);
$user->setEmail($data['user_email']);
$user->setUsername($data['user_email']);
$user->setPlainPassword($data['user_password']);
$user->setEnabled(false);
$user->setConfirmationToken($userUtils->generateToken());
$user->addRole('ROLE_USER');
$em->persist($user);
$em->flush();
}
Would it be smart to pass the $userManager and $userUtils objects in the controller when using the method?

I think the better is to override the FosUser Controller Action (Register for example) and put your code in a specific service.
The symfony2 doc give a great sample: http://symfony.com/doc/current/cookbook/bundles/inheritance.html

Related

difference between get('doctrine'); and getDoctrine();

In Symfony, I found three ways for accessing doctrine service and entity manager as follow:
$em = $this->getDoctrine()->getManager();
$em = $this->get('doctrine')->getEntityManager();
$em = $this->container->get('doctrine.orm.entity_manager');
Can anyone kindly explain their differences please and explain when should we use which of them.
The first one is only available when extending the base controller. It's a shortcut to doing $this->get('doctrine'), as you can see in the source:
public function getDoctrine()
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application.');
}
return $this->container->get('doctrine');
}
$this->get('doctrine') is also only available in controllers. get is also defined in the base controller and is a shortcut for $this->container->get():
public function get($id)
{
return $this->container->get($id);
}
$this->container->get('doctrine') is the fully written form of getting the doctrine registry.
$this->get('doctrine') its the method to use services,
And in symfony you have shortcutes to call this service $this->getDoctrine()

symfony : best way to set a user with a relationship

I'm very new in development and symfony. I wonder me what's the best way to code this below.
I've 2 entities (user and account). There is a relation between them (create an account requiers a user).
I wonder me what is the best way to set the user in account entity (prepersist, controller, __construct) when I'm adding an new account ?
PREPERSIST
First, I didn't find anything to set the user with prepersit method. Is there a way ?
Something like that :
/**
* #ORM\PrePersist
*/
public function prePersist()
{
$this->user = $this->get('security.context')->getToken()->getUser();
$this->updatedAt = new \Datetime("now");
$this->isActive = false;
}
CONTROLLER
...
$user = new User();
$account = new Account();
$account->setUser($user);
...
CONSTRUCTOR
/* Entity account */
...
public function __construct($user)
{
$this->user = $user;
}
...
/* Controller account */
...
$account = new Account($this->get('security.context')->getToken()->getUser())
...
Hope you can help me.
Based on your code above, you don't need to hook into a doctrine event to accomplish what you want. You can create the association in the controller before persisting the Account object.
If you are using the Symfony security component, obtaining the user in the controller is as simple as $this->getUser(). You can inject User via the Account constructor method __construct($user) or a setter method setUser($user). Although the constructor method is more efficient, either way is correct.
And to persist the Account object to your database from within the controller:
$em = $this->getDoctrine()->getManager();
$em->persist($account);
$em->flush();
I would recommend creating Doctrine2 Listener / Subscriber and register it as a service, than listen to prePersist event of Account entity. You can inject any other needed services in listeners / subscribers.
All information you need can be found on: http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html

How to use FOSRestBundle manual routes with CRUD from Symfony2?

I am using the FOSRestBundle to create a REST application but since REST features is only a part, I am also using some of Symfony2 built-in automation tools to generate my CRUD code. Everything works fine but I am unable to correctly map the route and I will appreciate some insight and example on how to do this manually. I have read the manual route definition in the FOS manual stating to use the given annotations but how do I do this since the CRUD code created by Symfony2 uses a different annotation?
Here is an example:
class UserController extends Controller
{
/**
* Lists all User entities.
*
* #Route("/", name="user")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyWebServicesBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
FOSRest manual gives the annotation for GET as
use FOS\RestBundle\Controller\Annotations\Get;
/**
* GET Route annotation.
* #Get("/likes/{type}/{typeId}")
*/
When I use the route as /index, it gives me an error and my route definition in config.yml is:
index:
type: rest
resource: Company\WebservicesBundle\Controller\UserController
How can I fix this problem?
If I were you, I would create separate bundles for your REST controllers and your generic CRUD controllers. This will make things easier to maintain (in my opinion). For example, create a AcmeMainBundle and a AcmeRestBundle, and then create a separate class to actually perform the actions that you will call from both bundles. Something like this:
// src/Acme/MainBundle/Crud/User.php (create this folder structure)
class User{
private $em;
public function __construct($em){
$this->em = $em;
}
public function getUser($id){
return $this->em->getRepository('AcmeMainBundle:User')->find($id);
}
}
Then:
// src/Acme/MainBundle/Controller/UserController.php
use Symfony\Component\HttpFoundation\Request;
use Acme\MainBundle\Crud\User;
class UserController extends Controller {
public function getAction($request){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($request->query->get('user_id'));
// return response
}
}
And then:
// src/Acme/RestBundle/Controller/UserController.php
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Routing\ClassResourceInterface;
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller implements ClassResourceInterface {
/**
* #Rest\View()
*/
public function getAction($id){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($id);
// return using the default format defined in config.yml
return array(
"success"=>'true',
"user" => $user
);
} // get_user [GET] /users/{id}
}
Please note that using the ClassResourceInterface means your method names will be used to generate the routes. see FOSRestBundle Docs for more info on that.
You can do something similar to this for all your CRUD, that way you keep your routes separate and maintainable, but still have a single code base to update.

FOSUserBundle: Get EntityManager instance overriding Form Handler

I am starting with Symfony2 and I am trying to override FOS\UserBundle\Form\Handler\RegistrationFormHandler of FOSUserBundle.
My code is:
<?php
namespace Testing\CoreBundle\Form\Handler;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseHandler;
use Testing\CoreBundle\Entity\User as UserDetails;
class RegistrationFormHandler extends BaseHandler
{
protected function onSuccess(UserInterface $user, $confirmation)
{
// I need an instance of Entity Manager but I don't know where get it!
$em = $this->container->get('doctrine')->getEntityManager();
// or something like: $em = $this->getDoctrine()->getEntityManager
$userDetails = new UserDetails;
$em->persist($userDetails);
$user->setId($userDetails->getId());
parent::onSuccess($user, $confirmation);
}
}
So, the point is that I need an instance of Doctrine's Entity Manager but I don't know where/how get it in this case!
Any idea?
Thanks in advance!
You should not use EntityManager directly in most of the cases. Use a proper manager/provider service instead.
In case of FOSUserBundle service implementing UserManagerInterface is such a manager. It is accessible through fos_user.user_manager key in the service container (which is an allias to fos_user.user_manager.default). Of course registration form handler uses that service, it is accessible through userManager property.
You should not treat your domain-model (i.a. Doctrine's entities) as if it was exact representation of the database-model. This means, that you should assign objects to other objects (not their ids).
Doctrine is capable of handling nested objects within your entities (UserDetails and User objects have a direct relationship). Eventually you will have to configure cascade options for User entity.
Finally, UserDetails seems to be a mandatory dependency for each User. Therefore you should override UserManagerInterface::createUser() not the form handler - you are not dealing with user's details there anyway.
Create your own UserManagerInterface implementation:
class MyUserManager extends \FOS\UserBundle\Entity\UserManager {
/**
* {#inheritdoc}
*/
public function createUser() {
$user = parent::createUser();
$user->setUserDetails(new UserDetails());
// some optional code required for a proper
// initialization of User/UserDetails object
// that might require access to other objects
// not available inside the entity
return $user;
}
}
Register your own manager as a serive inside DIC:
<service id="my_project.user_manager" class="\MyProject\UserManager" parent="fos_user.user_manager.default" />
Configure FOSUserBundle to use your own implementation:
# /app/config/config.yml
fos_user:
...
service:
user_manager: my_project.user_manager

Creating an admin user using datafixtures and fosuserbundle

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

Resources