Autowiring problem with parameter injection in Symfony 5 - php

I'm having troubles trying to retrieve a manager from a controller in Symfony 5.
I've got this MailerManager in src/Manager/MailerManager.php:
<?php
namespace App\Manager;
use App\Client\MailjetClient;
class MailerManager {
private $mailjetClient;
function __construct(MailjetClient $mailjetClient) {
$this->setMailjetClient($mailjetClient);
}
function send($data) {
}
function getMailjetClient() {
return $this->mailjetClient;
}
private function setMailjetClient($mailjetClient) {
$this->mailjetClient = $mailjetClient;
}
}
This manager needs to inject src/Client/MailjetClient.php in order to work, that has got this code:
<?php
namespace App\Client;
use \Mailjet\Resources;
class MailjetClient {
private $client;
function __construct(string $apikey, string $apisecret) {
$this->setClient($apikey, $apisecret);
}
function getClient() {
return $this->client;
}
function setClient($apikey, $apisecret) {
$this->client = new \Mailjet\Client($apikey, $apisecret);
}
}
This is just a wrapper for the mailjet sdk installed via composer, that needs to be feeded with different $apikey and $apisecret depending on the environment, for what I'm using parameters through services.yaml file, where I also have got autowiring enabled and service definitions for both MailjetClient and MailerManager:
parameters:
rabbitmq:
host: '%env(RABBITMQ_HOST)%'
port: '%env(RABBITMQ_PORT)%'
user: '%env(RABBITMQ_USER)%'
pwd: '%env(RABBITMQ_PWD)%'
mailjet:
apikey: '%env(MAILJET_APIKEY)%'
apisecret: '%env(MAILJET_APISECRET)%'
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Client\MailjetClient\:
resource: '../src/Client/MailjetClient.php'
arguments:
$apikey: '%mailjet.host%'
$apisecret: '%mailjet.port%'
App\Manager\MailerManager\:
resource: '../src/Manager/MailerManager.php'
arguments:
$mailjetClient: '#client.mailjet'
The problem I'm having is that I'm getting this error: Cannot autowire service "App\Client\MailjetClient": argument "$apikey" of method "__construct()" is type-hinted "string", you should configure its value explicitly. when I try to inject the MailManager in the src/Controller/MailerController.php controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use App\Manager\MailerManager;
class MailerController extends AbstractController
{
/**
* #Route("/compose", name="compose")
*/
public function compose(MailerManager $mailerManager)
{
dump($mailerManager);die();
}
}
}
What could posibbly be wrong? I'm coming from Symfony 2, and this parameter injection was something standard that used to work like a charm, now I'm totally confused about how to mix the autowiring with the service manual definition.

I had a total disaster on the services.yml side, thanks to u_mulder and Tejas Gosai hints I could finally put this to work. I had a few type errors on the parameters I was injecting, the MailManager.php specific declaration was not needed, and my client namespaces should not end with \ ad u_mulder suggested.
Finally, with this services.yml it works:
parameters:
rabbitmq.host: '%env(RABBITMQ_HOST)%'
rabbitmq.port: '%env(RABBITMQ_PORT)%'
rabbitmq.user: '%env(RABBITMQ_USER)%'
rabbitmq.pwd: '%env(RABBITMQ_PWD)%'
mailjet.apikey: '%env(MAILJET_APIKEY)%'
mailjet.apisecret: '%env(MAILJET_APISECRET)%'
services:
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Client\MailjetClient:
arguments:
$apikey: '%mailjet.apikey%'
$apisecret: '%mailjet.apisecret%'
App\Client\RabbitClient:
arguments: ['%rabbitmq.host%','%rabbitmq.port%','%rabbitmq.user%','%rabbitmq.pwd%']

Related

How to pass dynamic values to container in Symfony 5

How passing a dynamic variable from controller to a service? I want manage some istance in the constructor of my service that depend by json value. My service take two parameters in the construct: a service and a variable with the JSON.
For the first one, i have passed it directly in the service.yaml. For the second one, i have some difficult.
In the controller, i get from a API the JSON. But this json it can be null.
class IndexController extends AbstractController
{
/**
* #Route("/converter-hl7", name="converter", methods={"POST"})
*/
public function index(Request $request, $myjson = null) {
$myjson = $request->getContent();
global $kernel;
$converter = $kernel->getContainer()->get('app.converter');
$xml = $converter->outputXML();
$response = new Response($xml);
$response->headers->set('Content-Type', 'xml');
return $response;
}
I stock the JSON in my .env file, MYJSON=null.
This is my service.yaml
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/Tests/'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller/'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
# explicitly configure the service
app.error:
class: App\Service\Error
public: true
autowire: true
app.converter:
class: App\Service\ConverterHl7Refacto
public: true
autowire: true
arguments:
$error: '#app.error'
$json: '%env(MYJSON)'
So, in my service called ConverterHl7Refacto.php, i have the two parameters in the constructor. I would like manage the istances if the json is empty or non. If i do a dd() of $json, i get '%env(MYJSON)' instead JSON. Why?
class ConverterHl7Refacto
{
private $ricetta;
private $identificativiDocumento;
private $codiceDocumento;
private $infoDocumento;
private $assistiti;
private $partecipanti;
private $relatedDoc;
private $structuredBody;
private $root;
private $error;
public function __construct(string $json,Error $error) {
$this->error = $error;
if ($json){
$this->ricetta = new Ricetta(json_decode($json));
$this->root = new Root();
$this->identificativiDocumento = new Identificativi();
$this->codiceDocumento = new CodiceDocumento($this->ricetta);
$this->infoDocumento = new InfoDocumento($this->ricetta);
$this->assistiti = new Assistiti($this->ricetta);
$this->partecipanti = new Partecipanti($this->ricetta);
$this->relatedDoc = new RelatedDocument($this->ricetta);
$this->structuredBody = new StructuredBody($this->ricetta);
}
}
Nothing particularly tricky about the factory concept. A factory is basically used to create an instance of a given type. I did not test the following code so apologies for typos:
class Error {}
class Converter {
public function __construct(string $json, Error $error)
{
// Whatever
...
class ConverterFactory {
private $error;
public function __construct(Error $error) {
$this->error = $error;
}
public function create(string $json) : Converter {
return new Converter($json,$this->error);
}
}
class MyController {
public function action(ConverterFactory $converterFactory)
{
$json = $request->getContent();
$converter = $converterFactory->create($json);
You will need to exclude your Converter class from autowire in your services.yaml file. But that should be all you need. No need to explicitly define things like app.converter as autowire will take care of all that.

Symfony event subscriber not working even service is defined

I have an event subscriber:
namespace App\EventListener;
use App\Entity\Token;
use App\Model\EncryptUtils;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\Common\EventSubscriber;
class DatabaseSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(
'prePersist'
);
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if ($entity instanceof Token) {
$enityt->setCreatedAt(new \DateTime());
}
}
}
And I've declared the service in services.yaml:
parameters:
locale: 'en'
services:
App\EventListener\DatabaseSubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$appSecret: '%kernel.secret%'
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
But there's no way to get it working. I've also tried with an event listener but nothing happens
You should declare your service after all the default configuration because if not, the default configuration is overriding yours.
So your services.yaml file should be:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$appSecret: '%kernel.secret%'
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\EventListener\DatabaseSubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }

Symfony 4: Form as service, inject service : too few arguments

I try to get service in my Form, following official instructions:
https://symfony.com/doc/current/form/form_dependencies.html
this is my services.yaml:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false
class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Service\:
resource: '../src/Service/*'
App\Form\:
resource: '../src/Form/*'
my form look like this :
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\Contrat;
use App\Entity\Society;
use App\Entity\Client;
use App\Form\DataTransformer\ClientToNumberTransformer;
use App\Form\DataTransformer\SocietyToNumberTransformer;
class ContratType extends AbstractType{
private $manager;
public function __construct(SocietyToNumberTransformer $manager){ // this is the breakpoint from error
$this->manager = $manager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('...')
...
;
// Old fashion way for testing only ...
$builder->get('client')->addModelTransformer(new ClientToNumberTransformer($this->manager));
$builder->get('society')->addModelTransformer(new SocietyToNumberTransformer($this->manager));
}
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => Contrat::class,
));
}
It's look-like pretty ... When i test this with postman I get this error :
Too few arguments to function App\Form\ContratType::__construct(), 0
passed in /code/vendor/symfony/form/FormRegistry.php on line 92 and
exactly 1 expected
I don't understand, i found my fomr in autowiring debug command :
php bin/console debug:autowiring
App\Form\ContratType
App\Form\DataTransformer\ClientToNumberTransformer
App\Form\DataTransformer\SocietyToNumberTransformer
and the controller :
public function addContrat(Request $request, FormData $formData){
$em = $this->getDoctrine()->getManager('default');
$data = $formData->getRequestData($request);
var_dump($data);
$contrat = new Contrat;
$form = $this->factory->createBuilder(ContratType::class, $contrat)->getForm();
$form->submit($data);
if($form->isValid()){
$em->persist($contrat);
$em->flush();
return $this->response(
$this->serialize(
$contrat,
['group1']
)
);
}
return $this->response('error', 500);
}
thx for your help.
Try to replace
$form = $this->factory->createBuilder(ContratType::class, $contrat)->getForm();
for
$form = $this->createForm(ContratType::class, $contrat);
https://symfony.com/doc/current/forms.html#creating-form-classes
I have got the same problem but as I build my form from inside a service, I m still stuck ;-)
Hope it helps

Sylius/Symfony 3 inject service in a service

I created a service to extend the menu in admin of Sylius. It's work well ;)
I follow the official doc
I try to inject the router service in, but I've this following error :
Type error: Too few arguments to function
XXMenuListener::__construct(), 0 passed in
appDevDebugProjectContainer.php on line 1542 and exactly 1 expected
The declaration of this service :
services:
app.listener.admin.menu_builder:
class: XXX\Menu\AdminMenuListener
autowire: true
arguments:
- '#router'
tags:
- { name: kernel.event_listener, event: sylius.menu.admin.main, method: addAdminMenuItems }
and the service himself :
<?php
namespace XXX\Menu;
use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
final class AdminMenuListener
{
private $router;
public function __construct(Router $router){
$this->router = $router;
}
/**
* #param MenuBuilderEvent $event
*/
public function addAdminMenuItems(MenuBuilderEvent $event){
$menu = $event->getMenu();
$newSubmenu = $menu
->addChild('new')
->setLabel('XXX')
;
$newSubmenu
->addChild('new-subitem')
->setLabel('XXX')
//->setUri('https://www.google.com');
->setUri($this->router->generate('foo'))
;
}
}
What is wrong in ? Thanks for your help!
I think you need to clear cache if not helped to clean the cache directory manually.
In any case, you don't need a router service because menubuilder already has it.
For example:
for uri
$newSubmenu
->addChild('new-subitem')
->setLabel('XXX')
->setUri('https://www.google.com')
;
for route
$newSubmenu
->addChild('new-subitem', ['route' => 'foo'])
->setLabel('XXX')
;
If you use autowire to true you don't need to specify the router service. Something like this should be enough :
services:
app.listener.admin.menu_builder:
class: XXX\Menu\AdminMenuListener
autowire: true
tags:
- { name: kernel.event_listener, event: sylius.menu.admin.main, method: addAdminMenuItems }
In any case, your error indicates that you don't have any arguments. May be it's a caching issue or may be you have another service declaration for the same class XXX\Menu\AdminMenuListener without autowire to true and without arguments.

Non-existent service error since symfony 3.3

I had 2 working services at my symfony 3.2.(8?) project and had to up to 3.3 (currently 3.3.2). One of my services is working just fine, and the second one is giving me error:
services.yml
parameters:
#parameter_name: value
services:
_defaults:
autowire: true
autoconfigure: true
public: false
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository}'
list_brands:
class: AppBundle\Service\ListBrands
arguments: [ '#doctrine.orm.entity_manager' ]
calls:
- method: getBrands
picture_upload:
class: AppBundle\Service\UploadPicture
arguments: ['#kernel']
src\AppBundle\Service\UploadPicture.php
<?php
namespace AppBundle\Service;
use DateTime;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpKernel\Kernel;
class UploadPicture
{
protected $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
public function uploadPicture($object, string $oldPic, string $path)
{
/** #var UploadedFile $image */
$image = $object->getImage();
$time = new DateTime('now');
if ($image) {
$imgPath = '/../web/' . $path;
$filename = $time->format('d-m-Y-s') . md5($time->format('s')) . uniqid();
$image->move($this->kernel->getRootDir() . $imgPath,$filename . '.png');
$object->setImage($path . $filename . '.png');
} else {
$object->setImage($oldPic);
}
}
}
Error: You have requested a non-existent service "picture_upload".
Calling it like this: $uploadService = $this->get('picture_upload');
You have not written how you inject / call your service, but calling $this->get() sounds like a call from within a controller. I guess this is related to the new change in Symfony and your default service config of the public attribute.
Please check the following commented lines in your configuration:
# services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: false # here you are setting all service per default to be private
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository}'
list_brands:
class: AppBundle\Service\ListBrands
arguments: [ '#doctrine.orm.entity_manager' ]
calls:
- method: getBrands
picture_upload:
class: AppBundle\Service\UploadPicture
arguments: ['#kernel']
public: true # you need to explicitly set the service to public
You need to mark the service as public, either per default (not recommended) or explicitly in the service definition.

Categories