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
Related
I have user entity with custom repository for it. I have created an event listener to check if given user is authorized to access given game account.
To this I have created custom function hasAccountOfId($id) in my user repository. However I have problem with accessing this function from the event listener.
User entity:
/**
* #ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/.../
/**
* #ORM\OneToMany(targetEntity="Account", mappedBy="user")
*/
protected $accounts;
User entity custom repository:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
/**
* UserRepository
*/
class UserRepository extends EntityRepository
{
public function hasAccountOfId($accountId)
{
foreach ($this->accounts as $account) {
if ($account.id == $accountId) {
return true;
}
}
return false;
}
}
And the listener where I am struggling with accessing hasAccountOfId function.
// Account is set, verify if user is authorized
$id = $this->session->get("accountId");
$user = $this->token_storage->getToken()->getUser();
$userEm = $this->em->getRepository('AppBundle:User');
if(!$user->hasAccountOfId($id))
{
// Not authorized
die("Not authorized");
}
returns:
Attempted to call an undefined method named "hasAccountOfId" of class
"AppBundle\Entity\User".
while using !$userEm->hasAccountOfId($id) results in:
Notice: Undefined property: AppBundle\Entity\UserRepository::$accounts
How may I access my user repository for $user = $this->token_storage->getToken()->getUser() ?
Firstly for this simple OneToMany association You need to provide the Owning side of relation (in Account class). By creating new Account for User You will need to call $account->setUser($user); to make the Relation.
Secondly You don't need a custom method in Your RepositoryClass as You will get Accounts from Doctrine's Relation: $user->getAccounts().
The iteration through the Accounts to look for ID should be done NOT in RepositoryClass or Entity itself, but using a Manager Service or something else, because other way will violate Singe Responsibility Principle.
And finally, there is no . for class properties or methods, it must be ->. Note if statement in hasAccountOfId method.
Im trying to add entity manager to my service layer in Symfony. I've researched online but none of the answers seem to work for me.
I have a class with a namespace called AppBundle\Controller. My class name is MasterController. Then what exactly do I need to add into my services.yml?
Other than that I think I only need to pass EntityManager $em into the constructor as a param and then assign $em to $this->em (private)?
E.g.
class MasterController extends Controller
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
}
It's possible to define controllers as services while at the same time, extending the framework controller class. Why do this? By doing this you can take advantage of the base functionality such as the form factory, template rendering, get user stuff etc.
At the same time, you can inject any controller specific services you need. This avoids using the container as a service locator pattern. In other words, we can push services into the controller as opposed to having the controllers pull the services. The controllers don't need to care where the services come from and become more self contained.
Example:
class PersonController extends Controller
{
private $personRepository;
public function __construct($personRepository)
{
$this->personRepository = $personRepository;
}
public function editAction(Request $request, $id)
{
$person = $this->personRepository->find($id);
$form = $this->createForm(new PersonFormType(), $person);
$form->handleRequest($request);
if ($form->isValid()) {
// Note: I add flush/persist methods to my repositories
// You could inject the entity manager and flush from it
$personRepository->flush();
return $this->redirect($this->generateUrl('wherever'));
}
return $this->render('person/edit.html.twig', array(
'form' => $form->createView(),));
}
}
Make the controller a service with:
services:
person_repository:
class: AppBundle\Entity\PersonRepository
factory_service: 'doctrine.orm.default_entity_manager'
factory_method: 'getRepository'
arguments:
- 'AppBundle\Entity\Person'
person_controller:
class: AppBundle\Controller\PersonController
calls:
- [setContainer,['#service_container']]
arguments:
-'person_repository
So basically, you end up with a standard Symfony controller with all the standard functionality but you can inject controller specific services such as repositories which in turn makes your controllers easier to read and debug. You should never need to use $this->get('service') from within your controller action methods.
Contract this with the more standard method of retrieving an entity:
$personRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle\Entity\Person');
As you can see, the standard method not only ties the controller directly to doctrine but also requires the controller to know the entity's class name. You can decide which method is easier to write, understand and maintain.
First of all if your MasterController extends Symfony\Bundle\FrameworkBundle\Controller class then you have already EntityManager available by using:
$this->em = $this->getDoctrine();
But if you want to inject it by yourself to your constructor (and have Controller as a service) all you need to do is have entry in your services.yml like this:
services:
your_controller_service:
class: AppBundle\Controller\MasterController
arguments: [#doctrine.orm.entity_manager]
And that's it.
EDIT:
Remember that you need to have use Doctrine\ORM\EntityManager somewhere before your class definition to avoid Catchable fatal error
What is the best way to access configuration values inside an entity in a symfony 2 application?
I've searched about this and i've found two solutions:
Define the entity as a service and inject the service container to access configuration values
And this approach which defines a class in the same bundle of the entity with static methods that allows to get the parameter value
Is there any other solution? What's the best workaround?
Your entity shouldn't really access anything else, apart from associated entities. It shouldn't really have any connection outwardly to the outside world.
One way of doing what you want would be to use a subscriber or listener to listen to the entity load event and then pass that value in to the entity using the usual setter.
For example....
Your Entity
namespace Your\Bundle\Entity;
class YourClass
{
private $parameter;
public function setParameter($parameter)
{
$this->parameter = $parameter;
return $this;
}
public function getParameter()
{
return $this->parameter;
}
...
}
Your Listener
namespace Your\Bundle\EventListener;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Your\Bundle\Entity\YourEntity;
class SetParameterSubscriber implements EventSubscriber
{
protected $parameter;
public function __construct($parameter)
{
$this->parameter = $parameter;
}
public function getSubscribedEvents()
{
return array(
'postLoad',
);
}
public function postLoad(LifecycleEventArgs $args)
{
/** #var YourEntity $entity */
$entity = $args->getEntity();
// so it only does it to your YourEntity entity
if ($entity instanceof YourEntity) {
$entity->setParameter($this->parameter);
}
}
}
Your services file.
parameters:
your_bundle.subscriber.set_parameter.class:
Your\Bundle\EventListener\SetParameterSubscriber
// Should all be on one line but split for readability
services:
your_bundle.subscriber.set_parameter:
class: %your_bundle.subscriber.set_parameter.class%
arguments:
- %THE PARAMETER YOU WANT TO SET%
tags:
- { name: doctrine.event_subscriber }
You shouldn't need a configuration in your entity.
For example you have File entity and you need to save a file represented by this entity to a disk. You need some parameter, let say "upload_dir". You can pass somehow this parameter to the entity and define a method inside this entity which saves a file to upload dir. But better way would be create a service which would be responsible for saving files. Then you can inject configurtion into it and in save method pass entity object as an argument.
I would like to access generateUrl in my entity class. You can access generateUrl in controller class like this:
$url = $this->generateUrl('your_route_name', array(/* parameters */));
Accoring do this article, I should try to 'create a service, and inject the router component in it'. Then I am reading Symfony Docs: Service Container and try configuration, but I still can not make it.
in app/config/config.yml
services:
router:
class: ???
arguments: ???
How can I make router as service?
update
Why I want to use generateUrl in Entity class?
I am using Eko/FeedBundle. It requires to implements getFeedItemLink() in Entity. I need to give URL as return value of this function.
The short answer is: you don't use the router inside entities and you don't create entities as services. You should create a separate service which is responsible for creating urls (if you wrote more about what you are trying achieve it would be simpler to give you more appropriate example).
For example:
use Symfony\Bundle\FrameworkBundle\Routing\Router;
class YourCustomUrlGenerator
{
private $router;
public function __construct(Router $router)
{
$this->router = $router;
}
public function generateUrl(YourEntity $entity)
{
// generate the url
}
}
Then define your service for DIC:
services:
custorm_url_generator:
class: Namespace\YourCustomUrlGenerator
arguments: [#router]
I'm using a YAML configuration to wire my dependencies, and I need to provide some runtime information to get a useful object back. I was going to run a setter method from my code once the object has been injected, but I Was wondering if there was a better way of doing it (or if there's something I'm missing).
This is the gist of my configuration:
services:
example_object : "myObject"
arguments : ["%object_parameter1%"]
parameters:
object_parameter1 : Some Static Data
object_parameter2 : #Rutime info required
For retrieving the current logged in user in any service, inject the security.context. In this case I use setter injection to simply user mock injection.
namespace Acme\ExampleBundle\Foo;
use Symfony\Component\Security\Core\SecurityContextInterface;
class MyService
{
private $param;
private $user;
public function __construct($param)
{
$this->param = $param;
}
/**
* Retrieve the current logged in user from the security context.
*/
public function setUserFromContext(SecurityContextInterface $context)
{
$this->user = $context->getToken()->getUser();
}
/**
* Set any user object.
*
* Usefull for testing, to inject a simple user mock.
*/
public function setUser($user)
{
$this->user = $user;
}
public function doSomething()
{
// do something with the user object
}
}
Define the service:
services:
my_service:
class: Acme\ExampleBundle\Foo\MyService
arguments: ["%object_parameter1%"]
calls:
- [ setUserFromContext, [#security.context] ]
You should not try to add dynamic values directly into the DI configuration. Symfony services configuration is reflected by compiled DI container and recompilation is very heavy operation.
If you do not want to couple your service with Symfony's security system directly, you can add your custom "user provider" service as a dependency. Then you will need to rewrite this service if the source of information will change. It may be also easily mocked.
You can also use a factory to inject a user object instead of user provider service.