Synthiatic
Synthiatic

Reputation: 261

Symfony and Api-Platform: Use Doctrine and other Services outside controllers

Hello Stackoverflow

We have a problem with Symfony and API-PLATFORM. We tried a lot of things, but couldn't find any solution. We had difficulties using the answers already given on stackoverflow and the git issues they have that are related to this question. This is why this is not a duplicate in any form, since those sadly didn't help us.

The problem is as follows. We have the standard structure in our src folder of api-platform.

- src
-- Annotation
-- Controller
-- Doctrine
-- Emails
-- Entity
-- EventListener
-- EventSubscriber
-- Filters
-- Repository
-- Security
-- Utils

So here is were our problem starts. We have a few classes which are just some utilities for easy use. Those are in the utils folder. This question is about a class in the Utils folder named Phone. (namespace App\Utils\Phone). The thing here is, that we need to use the EntityManager in this Utils class, because we need to store some of the data (Temporarily). We'll do this with the following code (Which works in controllers)

$em = $this->getDoctrine()->getManager();
$em->persist($smsStore);
$em->flush();

Now the error hits: Doctrine doesn't even exist here. It cannot be found. The question here is: How can we use the Doctrine EntityManager outside of the Controllers? We tried extending by using the default AbstractController, but this cannot be correct and doesn't work.

The same thing happens when we want to use our security class to get the current user. ($this->security->getUser()). Even when we import it like with a use. use Symfony\Component\Security\Core\Security;.

We hope we were clear with this question. We're a bit desperate because we're still looking for a solution after a few days. We use PHP version 7.1.3 and API-PLATFORM version 1.1 (composer.json)

EDIT 1: The does not exist is about an undefined method (getDoctrine). We tried a few things to implement this. For example using the AbstractController to extend the class. Another solution that worked is really pass the DoctrineManager with the function. This is however not a good practice.

EDIT 2: The AuthController snippet as requested. It shows how we use the Phone Utils. Could it be that I need to implement the EntityInterface at this function surrounding the following code?

    $phone = new Phone();
    $phone->overwriteUser($this->getUser());

    $phone->newRecipient("4917640733908");
    $phone->setBody("Hello, this is a test.");

Upvotes: 1

Views: 843

Answers (1)

Alexandre Tranchant
Alexandre Tranchant

Reputation: 4570

Entity manager could be handle with the dependency injection(DI) pattern. This pattern is fully integrated in Symfony. This is the fun and magic aspect of Symfony.

All classes implementing an interface are available with dependency injections. With Symfony4+, the autoconfigure is activated. It seems to be complex, but for the final developer, it's pretty easy. Here is a simple example to do it with your Phone classes which seems to be a model where you have implemented you business logic.

class Phone
{
    private $entityManager;

    //The entity manager will be provided to your constructor with the dependency injection.
    public function __construct(EntityManagerInterface $entityManager)
    {
       $this->entityManager = $entityManager;
    }

    public function save($smsStore)
    {
       //you own logic go here

       //here is the magic part, the entityManager is available, connected, ready to use. 
       $this->entityManager->persist($smsStore);
       $em->flush();
    }
}

When you'll call the Utils classes method, the DI will construct it automatically and you will be able to use its property, the entity manager in this case.

If you want to use you class in a controller, you can do it by adding the phone class in its constructor:

class AuthController extends AbstractController
{
    public function __construct(Phone $phoneUtils)
    {      
       $this->phoneUtils = $phoneUtils;
    }

    public function someAction()
    {
       //your own logic here

       //here is the magic part, the phone Utils is ready, it imports the entitymanager
       //when you want to save your smsStore, simply call it
       $this->phoneUtils->save($smsStore);
    }
}

You are using Api-Platform, no problem! You can reproduce this design pattern in other classes like your DataPersisters, or in your EventListeners. If you look at this example in the ApiPlateform documentation, you can see how author get the Swiftmailer manager in his event listener. Simply replace the swiftmailer by EntityManagerInterface or the Phone utils classes.

(In a controller, it exists a simpliest way to retrieve the phone utils class)

class AuthController extends AbstractController
{
    //Do That: ADD Phone $phoneUtils as an argument in the method
    public function someAction(Phone $phoneUtils)
    {
       //your own logic here
       //Do not do that: $phoneUtils = new Phone();

       //Magic! when you want to save your smsStore, simply call it
       $phoneUtils->save($smsStore);
    }
}

If you want to get an autowired class (like the security layer), you could do it. To find which classes could be autowired, there are some tips:

symfony console debug:autowiring
symfony console debug:autowiring Security
symfony console debug:autowiring AuthorizationChecker

It will return all the classes corresponding to your search.

(If you do not use the symfony executable, you could replace it by php bin/console debug:autowiring)

Upvotes: 1

Related Questions