Situation
I have a Symfony2 project. I want to create Doctrine objects during a Behat test. To that effect, I need to access the EntityManager from within my FeatureContext class.
My attempt
I have my FeatureContext class extend RawMinkContext, which in turn extends BehatContext.
I then try, as per the documentation to access the container, or the entitymanager.
class FeatureContext extends RawMinkContext
{
/**
* #Given /^I have some disciplines$/
*/
public function iHaveSomeDisciplines()
{
$em = $this->getEntityManager();
$container = $this->getContainer();
}
But neither of these work, because none of the classes FeatureContext inherits from have access to this. As far as I know, only Controller does.
Question
How can I get access to Doctrine from within my FooContext classes?
Inject the kernel into your context by:
Behat2
class FeatureContext extends RawMinkContext implements KernelAwareContext
Behat3
class FeatureContext extends RawMinkContext
{
use KernelDictionary
Then you can get the entity manager as follows:
$this->getKernel()->getContainer()->get('doctrine.orm.entity_manager');
Not sure how Behat tests work but is it possible to turn your class into a service and inject entity manager?
Related
I am using an entity class which extends ServiceEntityRepository like this:
class Sms extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Sms::class);
}
...
}
so when I need to persist an instance of the entity class in my controller file I need to pass the ManagerRegistry as the argument for my Entity class but I couldn't find a way to access the ManagerRegistry in my controller.
can anyone help?
The problem was that ServiceEntityRepository should be extended in a repository class, not an entity class.
as it is mentioned here, there is no good description for auto-generating repositories from an existing database.
This is my entity class with its annotations:
/**
* Sms
*
* #ORM\Table(name="sms")
* #ORM\Entity(repositoryClass="App\Repository\SMSRepository")
*/
class Sms
{ ... }
this line is very important:
#ORM\Entity(repositoryClass="App\Repository\SMSRepository")
another important thing is to removing Entity from excluding in the services.YAML file.
if you set a name for the repository of your entity class by annotation, by running this command you will have your repository generated:
php bin\console make:entity --regenerate
and you can simply write you complex queries in the repository file which is generated by the aforementioned command.
for calling methods of your repository class you can use this in your controller files:
$this->getDoctrine()->getRepository(EntityFile::class)->youFunctionNameInRepositoryFile()
be careful about the argument of the getRepository which is the Entity file, not the repository file.
I want to build a new form type to handle one of my problem.
But, using this code :
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\DataTransformerInterface;
/**
* Entity hidden custom type class definition
*/
class EntityHiddenType extends AbstractType
{
/**
* #var DataTransformerInterface $transformer
*/
private $transformer;
/**
* Constructor
*
* #param DataTransformerInterface $transformer
*/
public function __construct(DataTransformerInterface $transformer)
{
$this->transformer = $transformer;
}
I got this error :
Cannot autowire service "App\Form\Type\EntityHiddenType": argument
"$transformer" of method "__construct()" references interface
"Symfony\Component\Form\DataTransformerInterface" but no such service
exists. Did you create a class that implements this interface?
I tried to put autowire to off, but I can't inject an interface right ?
Why I can't autowire this Symfony interface ?
If you have your DataTransformer in a own class (which implements DataTransformerInterface) you have to inject this class (your implementation) - not the interface.
see https://symfony.com/doc/current/form/data_transformers.html#using-the-transformer
you could of course aliasing the interface for your specific implementation - but then you only could have this one DataTransformer when injecting the Interface.
Because DataTransformerInterface is not autowirable.
Try this, to see autowiring classes/interfaces
bin/console debug:autowiring
The only way to make it work is to create a service class which implements DataTransformerInterface
Inject your new service by configuring it in service.yml
I would like to know how I could avoid setter injection under the following use case.
I have a collection of tagged services:
interface EmailFormatter
class CvEmailFormatter implements EmailFormatter
class RegistrationEmailFormatter implements EmailFormatter
class LostPasswordEmailFormatter implements EmailFormatter
I use a CompilerPass to inject these into a mailer service by invoking its addEmailFormatter() method.
Each EmailFormatter instance requires some configuration that must be retrieved at run time from the current user:
/**
* #var FormatterConfig[]
* #ORM\OneToMany(targetEntity="AppBundle\Entity\FormatterConfig", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
*/
protected $formatterConfigs;
Right now I am checking to see if the user has created a configuration for a particular EmailFormatter inside the mailer service's addEmailFormatter() method. If they have then setConfig() injects the configuration object. If no config exists yet the EmailFormatter does not get added at all.
public function addEmailFormatter(EmailFormatter $formatter, $alias)
{
$formatterConfig = $this->user->getFormatterConfig($alias);
if (null !== $formatterConfig) {
$this->formatters[$alias] = $formatter;
$formatter->setConfig($formatterConfig);
}
}
Could this be a legitimate use case given I am working with a mutable collection of services?
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
I wanted to override the default Doctrine\ORM\EntityRepository class in my Symfony2 project so that I can have access to the #session service so that all of my repositories have access to a certain session variable if it is set.
On investigation it appeared to be less simple than I had hoped, as the EntityRepository is instantiated from within the Doctrine\ORM\EntityManager, and this class is instantiated itself using a static "create" method.
I followed the answer in Injecting dependency into entity repository but have hit a roadblock in actually implementing the custom manager class (specifically where the answer's author says "But since you're making a custom entity manager, you can wire it up to the service container and inject whatever dependencies you need").
I have defined my overridden EntityManager class, with an overridden "create" function and have also overridden the "getRepository" function. It is in this function that I believe I need to add the session to the Repository as it is created using a "setSession" method on my overridden EntityRepository class, but I am unsure as to how to actually get the session into the manager in the first place, as the other constructor arguments for the EntityManager class (Connection $conn, Configuration $config, EventManager $eventManager) are supplied in the Symfony\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension "ormLoad" method.
I have also specified
doctrine.orm.entity_manager.class: Me\MyBundle\Doctrine\ORM\EntityManager
in my config.yml file.
How can I have Symfony use my custom EntityManager class when creating repositories, and inject the session into it as well?
Florian, here, explained how to create repository via service:
my_service:
class: Doctrine\Common\Persistence\ObjectRepository
factory_service: doctrine # this is an instance of Registry
factory_method: getRepository
arguments: [ %mytest.entity% ]
You could add calls to invoke setSession (as deferred DI):
my_service:
...
calls:
- [setSession, ["#session"]]
Is this you're trying to do?
I ended up going with something slightly different:
I overrode the doctrine.orm.entity_manager.class parameter with my custom class which simple extended the default class with an additional $session parameter (complete with getter and setter), along with overridden create and getRepository functions (to return instances of my class instead of the default.
I then overrode the EntityRepository class and implemented a "getSession" method which returned
$this->_em->getSession();
and finally, in a custom event listener which has access to the entity manager, I called
$this->entityManager->setSession($session);
which gave me access to the session from every repository.
In Symfony 4+ you can just make it a ServiceEntityRepository and with autowiring there's no need for any services.yaml changes.
namespace App\Repository;
use App\Entity\YourEntity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class YourRepository extends ServiceEntityRepository
{
private $session;
public function __construct(ManagerRegistry $registry, SessionInterface $session)
{
$this->session = $session;
parent::__construct($registry, YourEntity::class);
}
public function findSomethingUsingSession()
{
$someValue = $this->session->get('some_index');
// ...
}
}
Then in your controller (for example)
$repository = $this->getDoctrine()->getRepository(YourEntity::class);
$result = $repository->findSomethingUsingSession();
Or use dependency injection (recommended)
public function someAction(YourRepository $repository)
{
$result = $repository->findSomethingUsingSession();
// ...
}