how and where to execute custom queries in symfony 4 - symfony

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.

Related

Symfony 3 service configuration providing dynamic configuration

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?

Creating Doctrine objects from a Behat Context class

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?

inform symfony of where repository classes are located

I see in the creating your own entity provider section that you put the UserRepository class in the Entity directory. Can you stray from this code organization? For example if i wanted a structure like this:
MyCompany
MyProject
MyBundle
Controller
DependencyInjection
Entity
Repositories
doctrine
UserRespository.php
Resources
doc
public
translations
views
Can you inform symfony about where to locate the UserRepository?
This would do:
/**
* #ORM\Entity(repositoryClass="MyCompany\MyBundle\Repositories\doctrine\UserRepository")
*/
class User
{
}
In reading your doc :
/**
* Acme\UserBundle\Entity\User
*
* #ORM\Table(name="acme_users")
* #ORM\Entity(repositoryClass="Acme\Entity\UserRepository")
*/
class User implements UserInterface, \Serializable
{
So just replace the path by the path you want :
* #ORM\Entity(repositoryClass="MyCompany\MyBundle\Repositories\doctrine\UserRepository")

Injecting #session into EntityRepository

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

How should I configure an object dynamically when using Symfony's DI?

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.

Resources