Symfony5 - autowiring with using interface as typhint - symfony

I have an interface:
<?php
interface FooInterface
{
function doSth();
}
and two classes that implements this interface:
<?php
class FooImplementationOne implements FooInterface
{
}
class FooImplementationTwo implements FooInterface
{
}
And now i want to autowire one of classes that implements FooInterface based on called endpoint:
<?php
class Service
{
public function __construct(
private FooInterface $interface
);
}
Is there any chance to achieve that using some kind of Resolver/Provider?
I've tried implementing class that implements ServiceSubscriberInterface (this didnt work at all) and another one class implementing ArgumentValueResolverInterface (that one worked for controllers, but obviously not for services :( )

Related

Problem to import a function in a file to a class controller in Symfony

I want to add my function handleParameters() into my AuthController but I had "Expected to find a class ... while importing services from resource... but it was not found!"
HandleParameters.php
<?php
function handleParameters(array $aRequired, &$aRes, $requestMethod) {...}
?>
AuthController.php
<?php
class AuthController extends Controller
{
public function getAuth()
{
require_once (dirname(__DIR__, 1) .'/Common/HandleParameters.php');
handleParameters($aRequired, $this->aRes, $_SERVER['REQUEST_METHOD']);
...
}
}
All your php file must be a class by default so if you have the error
"Expected to find a class"
It's because your HandleParameters.php isn't a class
You have two solutions.
If the function is specific to your AuthController you can just define it in your controller class the same way you are defining getAuth().
Otherwise: The other way is to define a specific class MyService as a service and then you can inject that service into the contructor of any class, controller you want.
You can use trait, for example:
- create a Tool/ folder in src/.
- create in this folder a file HandleParametersTrait.php, the name must end with Trait.
HandleParametersTrait.php
<?php
namespace App\Tool;
trait HandleParametersTrait {
public function handleParameters($message) {
echo $message;
}
}
AuthController.php
<?php
namespace App\Controller;
use App\Tool\HandleParametersTrait;
class AuthController extends Controller
{
use HandleParametersTrait;
public function getAuth()
{
$this->handleParameters('hi');
...
}
}

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

Use of an overloaded global class in a Symfony controller

I try to include the following code in a Symfony controller file:
namespace {
class LocalSoapClient extends \SoapClient {
function __doRequest($request, $location, $action, $version) {
//...............
}
}
}
namespace .....\Controller {
//.......
}
In the controller class, I try to use the overloaded class:
$service = new \LocalSoapClient($wsdl);
With the \, the class is supposed to be in the global namespace. But I get the error:
Class 1\LocalSoapClient does not exist in .......\FrontBundle/Controller/
Why?
If I use a single namespace instruction, and I overload the class before the Controller class, I get the same kind of error.
What can I do to be able to use my overloaded class?
Thanks in advance
There are 2 DON’Ts that you’re violating:
DON’T put more than one class into one file.
DON’T mix namespaces. Reason is, Symfony uses PSR-0/-4 autoloading and what you’re trying to do cannot work.
The solution is to simply create separate files with proper namespaces.
Put the following into src/Your/SomethingBundle/Soap (or wherever you like the file to live):
<?php
namespace Your\SomethingBundle\Soap;
class LocalSoapClient extends \SoapClient
{
// …
}
And your controller should just look like this:
<?php
namespace Your\SomethingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Your\SomethingBundle\Soap\LocalSoapClient;
class MyController extends Controller
{
public function myAction()
{
$soapClient = new LocalSoapClient();
// …
}
}

Access to symfony services in Behat extension

I'm writing a Behat extension meant to be used with Symfony and Symfony2Extension.
For some services, I need to inject services defined in the Symfony application. Is there a way to do that?
In your FeatureContext.php file, you need to implement KernelAwareInterface and define setKernel() method. Methods getParameter() and getService() are option and for demonstration purposes.
Example
namespace Football\TeamBundle\Features\Context;
use Behat\MinkExtension\Context\MinkContext;
use Behat\Symfony2Extension\Context\KernelAwareInterface;
use Symfony\Component\HttpKernel\KernelInterface;
class FeatureContext extends MinkContext implements KernelAwareInterface
{
private $kernel;
public function setKernel(KernelInterface $kernelInterface)
{
$this->kernel = $kernelInterface;
}
public function getParameter()
{
$myParameter = $this->kernel->getContainer()->getParameter('name_of_the_param');
}
public function getService()
{
$myService = $this->kernel->getContainer()->get('name_of_the_service');
}
}

Calling function from another controller

I have two controllers
DefaultController
class DefaultController extends Controller
{
public function indexAction()
{
ApiController
class ApiController extends Controller
{
public function getCategoryAction()
{
Now I want to call getCategoryAction from my DefaultController.
Is it impossible or how can I make it?
There is forwarding in Symfony2. So, you could do sth like that:
class DefaultController extends Controller
{
public function indexAction()
{
// #var Response $categoryListResponse
$categoryListResponse = $this->forward('YourBundle:Api:getCategory');
// ... further modify the response or return it directly
return $categoryListResponse;
}
}
Where $categoryListResponse is Response type and represents an HTTP response. So you could $categoryListResponse->getContent() from this response.
You can configure controller as a service and then use
$this->forward('controller.service.name')->methodOnTheController()
you can create you own services and then call them from all the controllers
see Symfony service container
You can extend ApController controller like this:
class ApiController extends DefaultController
{
public function yourAction()
{
$this->getCategoryAction();
}
}
Or even better create a MyClass class (if getCategoryAction is going to be reused) register as a service
It all depend what you want to achieve at the end

Resources