Jordan S
Jordan S

Reputation: 51

Access Doctrine within a custom service in Symfony4

Aware that there is a lot of information around the net regarding this, I am still having a lot of trouble getting this to work.

I have created a custom service:

<?php

namespace App\Service;

use Doctrine\ORM\EntityManagerInterface;
use App\Entity\AccommodationType;
use App\Entity\Night;

class AvailabilityChecks {

    private $em;

     public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function nightAvailable(string $RoomCode, string $NightDate) {


        $GetRoom = $this->em->getDoctrine()->getRepository(AccommodationType::class)->findOneBy([
                'RoomCode' => $RoomCode
            ]);

        $RoomQnt = $GetRoom->getNightlyQnt();

        $GetNight = $this->em->getDoctrine()->getRepository(Night::class)->findOneBy([
                'RoomCode' => $RoomCode,
                'NightDate' => $NightDate
            ]);

        $NumberOfNights = $GetNight->count();

        if($NumberOfNights<$RoomQnt) {
            return true;
        }
        else {
            return false;
        }

    }


}

and have put this in services.yaml:

AvailabilityChecks.service:
  class: App\Service\AvailabilityChecks
  arguments: ['@doctrine.orm.entity_manager']

So when I try and use this in my controller, I get this error:

Too few arguments to function App\Service\AvailabilityChecks::__construct(), 0 passed in /mypath/src/Controller/BookController.php on line 40 and exactly 1 expected

I just can't figure out why it's not injecting the ORM stuff into the constructor! Any help greatly appreciated

Upvotes: 0

Views: 1894

Answers (4)

Adrien Rosi
Adrien Rosi

Reputation: 132

In SF4, you no longer need to specify dependencies required by your custom service in the service.yaml file. All you have to do is to use dependency injection.

So remove config lines, and call your service directly in the controller method :

<?php

namespace App\Controller;

use App\Service\AvailabilityChecks ;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class AppController extends AbstractController
{
    public function index(AvailabilityChecks $service)
    {
        ...
    }
}

Having said that, i think you don't need custom service to do simple operations on database. Use repository instead.

Upvotes: 0

AythaNzt
AythaNzt

Reputation: 1057

In Symfony 4, you dont need to create it as services. This is automatically now. Just inject the dependencies what you need in the constructor. Be sure that you have autowire property with true value in services.yml (it is by default)

Remove this from services.yml:

AvailabilityChecks.service:
  class: App\Service\AvailabilityChecks
  arguments: ['@doctrine.orm.entity_manager']

You dont need EntityManagerInterface because you are not persisting anything, so inject repositories only.

<?php

namespace App\Service;

use App\Entity\AccommodationType;
use App\Entity\Night;
use App\Repository\AccommodationTypeRepository;
use App\Repository\NightRepository;

class AvailabilityChecks {

    private $accommodationTypeRepository;
    private $nightRepository

     public function __construct(
          AcommodationTypeRepository  $acommodationTypeRepository,
          NightRepository $nightRepository
    )
    {
        $this->acommodationTypeRepository = $acommodationTypeRepository;
        $this->nightRepository = $nightRepository;
    }

    public function nightAvailable(string $RoomCode, string $NightDate) {


        $GetRoom = $this->acommodationTypeRepository->findOneBy([
                'RoomCode' => $RoomCode
            ]);

        $RoomQnt = $GetRoom->getNightlyQnt();

        $GetNight = $this->nightRepository->findOneBy([
                'RoomCode' => $RoomCode,
                'NightDate' => $NightDate
            ]);

        $NumberOfNights = $GetNight->count();

        if($NumberOfNights<$RoomQnt) {
            return true;
        }
        else {
            return false;
        }

    }
}

Upvotes: 0

VladRia
VladRia

Reputation: 1587

The problem is in your BookController. Even though you didn't posted its code I can assume you create new AvailabilityChecks in it (on line 40).

In Symfony every service is intantiated by service container. You should never intantiate service objects by yourself. Instead BookController must ask service container for AvailabilityChecks service. How should it do it ?

In Symfony <3.3 we used generally :

use Symfony\Bundle\FrameworkBundle\Controller\Controller;


class MyController extends Controller 
{
    public function myAction() 
    {
        $em = $this->get('doctrine.orm.entity_manager');
        // ...
    }
}

Nowadays services can be injected in controllers using autowiring which is way easier:

use Doctrine\ORM\EntityManagerInterface;

class MyController extends Controller 
{
    public function myAction(EntityManagerInterface $em) 
    {
        // ...
    }
}

Upvotes: 3

dbrumann
dbrumann

Reputation: 17166

You are using the wrong service for what you want to do. The alias doctrine that is used, e.g. in the AbstractController when you call getDoctrine() is bound to the service Doctrine\Common\Persistence\ManagerRegistry.

So the code you wrote fits better with that and you should either add @doctrine or @Doctrine\Common\Persistence\ManagerRegistry to the service definition.

Both with your current configuration or the changed one, you don't have to call $this->em->getDoctrine(), because $this->em is already equivalent to $this->getDoctrine() from your controller. Instead you could create a (private) method to make it look more like that code, e.g.:

private function getDoctrine()
{
    return $this->em;
}

Then you can call $this->getDoctrine()->getRepository(...) or use $this->em->getRepository(...) directly.

Upvotes: 0

Related Questions