Symfony 4 autowiring in Entity not working - symfony

I`m trying to autowire in my User Entity Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface
and receiving error Too few arguments to function App\Entity\User::__construct().
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->packages = new ArrayCollection();
$this->passwordEncoder = $encoder;
}
In 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 # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# 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,Entity,Migrations,Tests,Kernel.php}'
# 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']

As stated in the comment, Entities are specifically not treated as services, therefore cannot use autowiring.
Entities are supposed to be used for simple data manipulations. If you need to access other Symfony components, one of your options is to create a manager class that handles the additional work or rely on event listeners.

Related

Naming a service changes dependency injection behavior in Symfony

I have a factory service class which needs access to Symfony´s (v6.0.2) service_container and is specified in the services.yaml as follows:
App\Service\SomeService:
public: true
arguments: ['%some_arguments%']
calls:
- [ setContainer, [ '#service_container' ] ]
It works fine and I can access the service container and all the other injected arguments from within my service as desired.
However, if I change the service definition into a named service like this - without changing anything else:
app.some_service:
class: App\Service\SomeService
public: true
arguments: ['%some_arguments%']
calls:
- [ setContainer, [ '#service_container' ] ]
I still have access to the injected arguments ("%some_arguments%"), but the service container suddenly becomes null.
I would expect the behavior of the dependency injection to be the same - regardless of whether I use a named service or not.
Does the DI behavior change as soon as I give my service a name or do I need to take another approach if I want to give my service a name?

Custom normalizer not passed name converter service

I'm working on creating a custom (de)normalizer to handle entities. I have created the normalizer and allowed the service container to autowire/autoconfig. The service is selected correctly during deserialization, but I'm having trouble with the name converter. I want to use the MetadataAwareNameConverter service since I'm using the #SerializedName annotation in my entity. No matter what I do, it is always null in the custom normalizer. I have tried a number of methods of getting the name converter service:
Setting it explicitly in my class constructor
Setting it in the service definition (effectively getting rid of autowire/autoconfig)
Setting MetadataAwareNameConverter as the default in framework.yaml (I discovered it is the default already).
Copied an existing normalizer into my src and renamed it to see if it got the correct name converter (it still didn't work)
Built in normalizers are getting a name converter without issue, it is just my custom normalizer that is having this issue.
Is there anything else I should try? Am I missing a step in setting up my service? Any direction is appreciated.
UPDATE - when I dump the service container, the name converter service is missing from the arguments list
---------------- ----------------------------------------------------------
Option Value
---------------- ----------------------------------------------------------
Service ID App\Normalizer\QNormalizer
Class App\Normalizer\QNormalizer
Tags serializer.normalizer
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
Arguments Service(serializer.mapping.class_metadata_factory)
-----THIS IS WHERE THE NAME CONVERTER SHOULD BE----
Service(property_accessor)
Service(property_info)
Service(serializer.mapping.class_discriminator_resolver)
Manually injecting MetadataAwareNameConverter in services.yaml solved problem for me.
App\Serializer\CustomNormalizer:
arguments:
$nameConverter: '#serializer.name_converter.metadata_aware'
I faced same issue.
In my case it was a missconfiguration of services happened because of framework was configured to autoconfigure services (It's default framework configuration).
In result I had my custom normalizer duplicated in list of services.
First one is autoconfigured without priority
Second one is declared by me and having name converter injected:
Service Id
Priority
Class Name
App\Adapter\Symfony\Serializer\Normalizer\TranslationNormalizer
App\Adapter\ApiPlatform\Serializer\Normalizer\ItemNormalizer
api_platform.serializer.normalizer.item
-895
App\Adapter\ApiPlatform\Serializer\Normalizer\ItemNormalizer
Declaration:
api_platform.serializer.normalizer.item:
class: App\Adapter\ApiPlatform\Serializer\Normalizer\ItemNormalizer
arguments:
$nameConverter: '#serializer.name_converter.metadata_aware'
autoconfigure: false
tags:
- {name: serializer.normalizer, priority: -895}
Since autoconfigured normalizer have higher priority in list - it was picked by serializer so my SerializedName annotation wasn't working.
Solution is to disable autoconfiguration for first on service:
App\Adapter\ApiPlatform\Serializer\Normalizer\ItemNormalizer:
autoconfigure: false

How to exclude Domain from autowiring when declaring service with Symfony 4.1.3?

I'm working on a light DDD app with Symfony 4. In my services.yaml file, I configured the autowiring as such:
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 # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# 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/{DataFixtures,Migrations,Tests,Domain/IceCream/IceCream.php,Domain/Cake/Cake.php,Domain/Candy/Candy.php}'
I excluded all the entities since they're not services. As you might have noticed, I listed all corresponding files because when I type :
exclude: '../src/{DataFixtures,Migrations,Tests,Domain}'
a runtime exception is raised: Cannot autowire service : "App\Application\Query\Cake\CakesQueryHandler": argument "$cakeRepository" of method "__construct()" references interface "App\Domain\Cake\CakeRepositoryInterface" but no such service exists. You should maybe alias this interface to the existing "App\Infrastructure\Doctrine\Repository\Cake\CakeRepository" service.
The first service, which is a queryhandler, is not autowired.
How can I exclude the whole Domain without having to type all files within it ?
Thank you for your help.
As you said to me directly, the runtime error you have is :
(1/1) RuntimeException
Cannot autowire service "App\Application\Query\Cake\CakesQueryHandler": argument "$cakeRepository" of method "__construct()" references interface "App\Domain\Cake\CakeRepositoryInterface" but no such service exists. You should maybe alias this interface to the existing "App\Infrastructure\Doctrine\Repository\Cake\CakeRepository" service.
In your query handler, you want to inject a service with is typed as App\Domain\Cake\CakeRepositoryInterface.
As a matter of fact, you have declared a service for your category repository with the name : App\Infrastructure\Doctrine\Repository\Cake\CakeRepository.
To fix this, you need to add an alias from your interface to your repository in your services.yaml file :
App\Domain\Cake\CakeRepositoryInterface: '#App\Infrastructure\Doctrine\Repository\Cake\CakeRepository'

Symfony service injection to action

I am trying to have the SwiftMailer service made available to me in a action using the following technique:
http://symfony.com/doc/current/service_container.html#fetching-and-using-services
public function submitAction (Request $request, \Swift_Mailer $mailer)
{
$mailer = $this->get('mailer');
var_dump($mailer);
}
This is the error:
Controller "AppBundle\Controller\QuoteController::submitAction()"
requires that you provide a value for the "$mailer" argument. Either
the argument is nullable and no null value has been provided, no
default value has been provided or because there is a non optional
argument after this one.
The var_dump tells me this is not empty - I recently upgraded to Symfony 3.3.10 and changes my services.yml to the following:
services:
_defaults:
autowire: true
autoconfigure: true
public: true
Services are normally (and can be automatically) injected into the constructor.
They can also be automatically put into the action method parameters, but [the controller must be tagged to enable it].(http://symfony.com/doc/current/service_container/3.3-di-changes.html)
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
tags: ['controller.service_arguments']
The Request (and since 3.2+, SessionInterface and User/UserInterface, among others) are detected with the in-built PHP Reflection api. There's a more specialised subsystem to be able recognise and automatically add them, with ArgumentResolver (using SessionValueResolver and ServiceValueResolver) - beyond the existing ParameterConverter (used for fetching entities from parameter IDs for the action method parameters).

Symfony2 initialize service [duplicate]

This question already has an answer here:
Symfony instantiate a service one time and use it with several users
(1 answer)
Closed 7 years ago.
i use a bundle (wa72/jsonrpc-bundle) as a service. I need to initialize a value just after the service is constructed, for all the application. I don't think i need a listener as i just need to set it once, not before each request.
here's what i need to do :
$wa72_jsonrpc = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
$wa72_jsonrpc->setSerializationContext(SerializationContext::create()->enableMaxDepthChecks());
how can i do this for all the application, just 1 time ?
If I'm not mistaking, a Kernel onRequest listener would do it if you want to expose the thing to every controller with no additional work.
Another way to do it, is to setup another Service that explicitly wraps your setup and exposes it to your app as a service
src/AppBundle/Services/jsonrpcService.php:
<?php
namespace AppBundle\Services;
class jsonrpcService
{
public $jsonrpc;
public function __construct($wa72_jsonrpc)
{
$wa72_jsonrpc->setSerializationContext(SerializationContext::create()->enableMaxDepthChecks());
$this->jsonrpc = $wa72_jsonrpc;
}
}
app/config/config.yml
services:
jsonrpc:
class: AppBundle\Services\jsonrpcService
arguments: [#wa72_jsonrpc.jsonrpccontroller]
in any controller just call:
$jsonrpc = $this->get('jsonrpc')->jsonrpc;
Is not so well documented but you should enable it in the bundle section in the config.yml as described here
If you use jms_serializer you can also configure exclusion strategies
(groups, version, or max depth checks) :
# app/config/config.yml
wa72_json_rpc:
functions:
myfunction1:
service: "mybundle.servicename"
method: "methodofservice"
jms_serialization_context:
group: "my_group"
version: "1"
max_depth_checks: true
Of course, seem you need to enable the jms serializer bundle also.
Hope this help

Resources