Symfony: Inject Doctrine Repositories in Controllers

I’ve come across a cool pattern in Symfony 4 – injecting repositories instead of EntityManager

// Before
public function showAllUsers(EntityManagerInterface $em)
    {
    $users = $em->getRepository(User::class)->findAll();

    return $this->json($users);
    }

// After
public function showAllUsers(UserRepository $userRepository)
    {
    $users = $userRepository->findAll();
    
    return $this->json($users);
    }

In the latter, we keep the scope limited only to the repository we need, not holding on to the whole EntityManager.
It’s possible because all your repositories are automatically registered as services and hence can be autowired.
Previous Symfony versions allowed you to register repositories as services manually and event that was tedious, because it required a custom factory etc.

Since EntityManager is mostly used for persisting and deleting entities, you can add a few helper methods to your repository(ies):

/**
 * @param object $entity
 */
public function save($entity)
    {
    $this->_em->persist($entity);
    $this->_em->flush();
    }

/**
 * @param object $entity
 */
public function delete($entity)
    {
    $this->_em->remove($entity);
    $this->_em->flush();
    }

$this->_em property is already available, if you’re extending the default ServiceEntityRepository

Then, you can shorten your create/delete controllers as well:

// Before
public function createUser(Request $request, EntityManagerInterface $em)
    {
    $user = new User();
    // request handling, form validation..
    
    $em->persist($user);
    $em->flush();

    return $this->json($user);
    }

public function deleteUser(User $user, EntityManagerInterface $em)
    {
    $em->remove($user);
    $em->flush();

    return new Response('', Response::HTTP_NO_CONTENT);
    }

// After
public function createUser(Request $request, UserRepository $userRepository)
    {
    $user = new User();
    // request handling, form validation..
    
    $userRepository->save($user);

    return $this->json($user);
    }

public function deleteUser(User $user, UserRepository $userRepository)
    {
    $userRepository->delete($user);

    return new Response('', Response::HTTP_NO_CONTENT);
    }

EDIT: first version of the post did not utilize the DI for fetching the EntityManager and did an unfair comparison. This is corrected and the actual difference between these two approaches is probably trivial now.