How can I override autowired symfony service? - php

I'm working on a Symfony 4 projet and I have read Symfony documentation about autowiring
My problem is not really about autowiring but importted files.
This Symfony documentation says that you can import your services in many files.
In my project, I have the service.yaml in the config folder which import all services in services folder
imports:
- { resource: './services/admin.yaml'}
- { resource: './services/front.yaml'}
- { resource: './services/core.yaml' }
services:
_defaults:
autowire: true
autoconfigure: true
So i need to override a service autowiring because it has two strings in constructor and I need to wire those arguments manually
extranet.form.data_subscriber.remove_empty_reference_fields:
class: Extranet\Admin\Form\DataSubscriber\RemoveEmptyFieldsSubscriber
arguments:
$collectionName: references
$childName: name
Extranet\Admin\Form\DataSubscriber\RemoveEmptyFieldsSubscriber: '#extranet.form.data_subscriber.remove_empty_reference_fields'
Extranet\Admin\Form\DataSubscriber\RemoveEmptyFieldsSubscriberInterface: '#Extranet\Admin\Form\DataSubscriber\RemoveEmptyFieldsSubscriber'
But now if I put this service directly in my services.yaml at the end of file it works.
My question is, how I can override autowiring with services not configured directly in the services.yaml
Let me know if you can help me, thanks
EDIT
I already tried to put the imports part at the end of services.yaml file

Related

Environment specific services config in Symfony 3.4

I need to access some private services via the container while in the dev environment.
I thought it would be simple - I'd just make them public specifically for dev like so:
app/config/services.yml:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
MyBundle\:
resource: '../../*'
exclude: '../../{Entity,Repository,DoctrineMigrations,DependencyInjection,Tests,Util}'
app/config/services_dev.yml:
imports:
- { resource: services.yml }
services:
_defaults:
autowire: true
autoconfigure: true
public: true
app/config/config_dev.yml
imports:
- { resource: config.yml }
- { resource: services_dev.yml }
This however does not yield the desired result: services behave as if private, config is not merged at all(e.g. when you have parameter injection, sy yells that I must define them explicitly, even if the parent config already does).
There's plenty of examples, in the default config of Sy, where specific parameters get overridden for various environments, but for some reason this does not seem to be the case for services. Is the services config processed in a special way somehow? Have I misunderstood something? What's going on here?
P.S. I know about dependency injection, I know accessing the container is considered bad practice, that's not the point of the question. Please do not suggest that.
It does not work, because your defaults only work on the services defined in your services_dev.yaml. That does not include the imports.
You could try a services_dev.yaml like this:
services:
_defaults:
autowire: true
autoconfigure: true
public: true
MyBundle\Services\MyPublicService: ~
This will overwrite the service and make it public. Obviously you have to replace the class with the actual service.
You do not need to import the other services. The kernel will take care of loading this class.

Looked for namespace "security", found none

I am trying to create a custom bundle that is using a special authentication service. This bundle will be used by all of our projects.
I want to make it so it is needed a little configuration to use it.
My problem appears when i'm trying to add a security config inside my package like so:
# security.yml
security:
providers:
specialauth:
id: AuthBundle\Security\SpecialAuthProvider
firewalls:
main:
logout:
path: '/logout'
When I do this inside my bundle I get this error:
Looked for namespace "security", found none
If I move this security configuration inside my app/config it works ok but I want this config to stay in the AuthBundle so the developers don't have to configure much stuff for every project.
Is this a restriction from symfony not allowing security configs from external bundles or what can the problem be?
You can import your security.yml inside the security file of the project:
app/config/security.yml :
imports:
- { resource: '#AuthBundle/Resources/config/security.yml' }

Symfony4 use external class library as a service

I have a little external library that expose many classes.
Into my symfony4 project I would like to declare my class from vendor, as a service with autowire and public.
So I have include my library with composer and add psr configuration like this into composer.json:
"autoload": {
"psr-4": {
"App\\": "src/",
"ExternalLibrary\\": "vendor/external-library/api/src/"
}
}
After that I have tried to change my services.yaml into symfony like this:
ExternalLibrary\:
resource: '../vendor/external-library/api/src/*'
public: true
autowire: true
If I launch tests or run the application returns me this error:
Cannot autowire service "App\Domain\Service\MyService": argument "$repository" of method "__construct()" references interface "ExternalLibrary\Domain\Model\Repository" but no such service exists. You should maybe alias this interface to the existing "App\Infrastructure\Domain\Model\MysqlRepository" service.
If I declare into services.yaml the interface this works fine:
ExternalLibrary\Domain\Model\Lotto\Repository:
class: '../vendor/external-library/api/src/Domain/Model/Repository.php'
public: true
autowire: true
But I have many classes and I don't want to declare each class, how can I fix services.yaml without declare every single service?
Thanks
You need to create services by hand:
I did not test it but it should look like this
services.yaml
Some\Vendor\:
resource: '../vendor/external-library/api/src/*'
public: true # should be false
Some\Vendor\FooInterface:
alias: Some\Vendor\Foo # Interface implementation
Some\Vendor\Bar:
class: Some\Vendor\Bar
autowire: true
php
<?php
namespace Some\Vendor;
class Foo implements FooInterface
{
}
class Bar
{
public function __construct(FooInterface $foo)
{
}
}
To be more precise you should have something like
ExternalLibrary\Domain\Model\Repository:
alias: App\Infrastructure\Domain\Model\MysqlRepository
Let's take Dompdf as an example :
When you try to add type-hint Dompdf in your action controller or service method , an error will be occurred saying that auto-wiring isn't possible because Dompdf is an external PHP library
So to solve this problem we'll make a little change in our services.yaml file by adding this short config
Dompdf\: #Add the global namespace
resource: '../vendor/dompdf/dompdf/src/*' #Where can we find your external lib ?
autowire: true #Turn autowire to true
Apply the above example to all external PHP libs :)
That's all !
I had the same problem and someone gave me this solution, which works fine for me:
Use an external repository with symfony4 trouble with autoload and parameters
I copy the other solution by user #DasBen here just in case:
I think that you don't have to import each service separately. Your are already doing that with the "Puc\SapClient" part.
The problem could be that you are importing your models, which should not be imported.
In the symfony example project there is this part vor "services.yaml":
# 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/{Bundle,DependencyInjection,Entity,Model,Migrations,Tests,Kernel.php}'
Then your part would be:
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
Puc\SapClient\:
resource: '../vendor/puc/sap-client/src/*'
exclude: ''../vendor/puc/sap-client/src/{Entity,Model,"etc."}'
"etc." Would be everything that is not needed as service.

Symfony 4 _instanceof in Bundle's services.yaml

I have a bundle which has interface Optimax\HealthCheckBundle\Service\HealthInterface
I need set tags to all services which implement this interface. I do it with the following directive:
_instanceof:
Optimax\HealthCheckBundle\Service\HealthInterface:
tags: ['health.service']
It works fine when I put this directive into config/services.yaml. But if I put this code into my bundle's config (which required via composer) vendor/optimax/health-check/src/Resources/config/services.yaml it doesn't work. I don't want copy-paste this directive into services.yaml every time when I require this bundle to a new project.
How can I move this directive into services.yaml which is in my Bundle's directory or at least into another file in config/packages folder of the project?
To expand on the issue for others.
_instanceof functions identically to the _defaults definition. In that the _instanceof definition only applies to the file it is used in.
This prevents third-party bundle definitions from affecting your entire application with definitions like:
_defaults:
public: true
autowire: false
_instanceof:
Psr\Log\LoggerAwareInterface:
- method: setLogger
arguments:
- '#custom_logger'
tags:
- { name: monologer.log, channel: 'custom_channel' }
Therefor if the service you are attempting to tag with _instanceof is not declared in the same services.yml file the tag will not be added.
To tag services that implements an Interface in the entire application, you would need to use the method provided by #Jeroen
Did you try auto tagging all services with this interface in your Bundle extension like this:
$container->registerForAutoconfiguration(CustomInterface::class)
->addTag('app.custom_tag')
;
Taken from the Symfony docs:
https://symfony.com/doc/current/service_container/tags.html

Symfony Bundle Load YML Configuration

Since I am new to Symfony and I couldn't manage to find some useful information in google I decided to write to you.
I've read about the way of loading custom DI alias information from a dependency injector in your bundle and how to create a Configuration class that will expose the alias structure. However I am to some extend confused how I can create a file, for example routing.yml, in my AcmeBundle/Resources/config/ folder and read the data from it. E.g:
some_alias:
resource: "#AcmeBundle/Controller/"
type: annotation
prefix: /
I want to make a bundle with routing, independent from the main configuration files in the app folder.
You can create your bundle routing.yml in your WhateverBundle/Resources/config/routing.yml and the in the app/routing.yml just include your bundle's routes.
mybundleorwhatever:
resource: "#WhateverBundle/Resources/config/routing.xml"

Categories