Symfony services.yaml env custom param condition - symfony

I have a specific variable in my .env.dev.local
SRV_INST=s01
Is there a way to use custom env variables in services.yaml in a conditional way similar to when#dev ? I would like to define some services only when SRV_INST=s01 (something like when %env(SRV_INST)% === 's01').
I can use services.php, but I wanted to know if there's a way to do this in services.yaml

you found a solution - but for anyone else, there are multiple other solutions for this:
services:
App\Mailer:
# the '#=' prefix is required when using expressions for arguments in YAML files
arguments: ["#=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'"]
or
services:
# ...
App\Mail\MailerConfiguration: ~
App\Mailer:
# the '#=' prefix is required when using expressions for arguments in YAML files
arguments: ['#=service("App\\Mail\\MailerConfiguration").getMailerMethod()']
you can find more information at https://symfony.com/doc/current/service_container/expression_language.html

Okay, I solved this by importing a .php additional configuration file.
// config/services.yaml
imports:
- { resource: 'services_conditional.php' }
And the services_conditional.php file:
// config/services_conditional.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use SomeCustom\Service\CustomDoubleBla;
return function (ContainerConfigurator $configurator) {
$services = $configurator->services();
if (!empty($_ENV['SRV_INST']) && $_ENV['SRV_INST'] === 's01') {
$services->set(CustomDoubleBla::class)
->decorate('custom_single_bla.service')
// pass the old service as an argument
->args([service('.inner')]);
}
};

Related

Is it possible to override only some arguments of a Symfony 4 service without compiler passes?

I'd like to override a service definition coming from a third-party bundle, which looks like this:
# vendor/some_vendor/some_bundle/src/Resources/config/config.yml
services:
SomeBundle\Service\ServiceClass:
arguments:
$arg1:
- 'abcd'
- 'efgh'
$arg2:
- '1234'
- '5678'
namespace SomeBundle\Service;
class ServiceClass
{
public function __construct(array $arg1 = [], array $arg2 = [], array $arg3 = [])
{
...
}
}
I'd like to override the config in my app/config/config.yml adding $arg3 argument,
but keeping the existing ones, $arg1 and $arg2.
When I do like this
# app/config/config.yml
# $arg1 and $arg2 config values are lost
services:
SomeBundle\Service\ServiceClass:
arguments:
$arg3:
- 'uvw'
- 'xyz'
$arg1 and $arg2 values are lost. So I have to copy the full set of original config values
from the third-party bundle and then add my additional config for $arg3:
# app/config/config.yml
# This, of course, works fine, but I'd like to avoid copying $arg1 and $arg2
services:
SomeBundle\Service\ServiceClass:
arguments:
$arg1:
- 'abcd'
- 'efgh'
$arg2:
- '1234'
- '5678'
$arg3:
- 'uvw'
- 'xyz'
So the question is, is it possible somehow to add or replace some arguments and keep values of other ones, just by overriding service in my config.yml, without doing complicated things like writing a compiler pass class
Yes, you can. But only if the third party bundle sets the arguments as parameters, then you can override them.

Symfony 3 - How inject own variable from config to service right way?

I want on my web create cache config with different cache values. I have working example:
// config.yml
parameters:
myValue:
first: 1
second: 2
// services.yml
my_repo:
class: AppBundle\Repository\MyRepository
factory: ["#doctrine.orm.entity_manager", getRepository]
arguments:
- 'AppBundle\Entity\My'
calls:
- [setValue, ["%myValue%"]]
// MyRepository.php
public function setValue($val) {
$this->first = $val['first'];
}
// Inside controller method
$someVariable = $this->get('my_repo')
->someOtherFunction();
But is this way correct? What if another programmer will call repository 'standart' way $em->getRepository('MyRepository')? It will crash on udefined variable... Is there way to do this for example via constructor? Or constructor is bad idea?
I am interested in the yours practice - better solution etc.
Something like
[setValue, ["#=container.hasParameter('myValue') ? parameter('myValue') : array()"]]
Should do the trick. Then just check in your service if the variable injected is empty or not. See the doc for more on the Expression language

External environment variable as array

How to set external environment variable as array?
If I have environment variable
SYMFONY__NSQLOOKUPD__HOSTS=["localhost:4161"]
and in config.yml:
socloz_nsq:
lookupd_hosts: %nsqlookupd.hosts%
Then I got an error:
Invalid type for path "socloz_nsq.lookupd_hosts". Expected array, but got string
I've found solution. Here it is:
in config.yml add to the imports section:
imports:
- { resource: parameters.php }
then create parameters.php file at the same directory where config.yml exists, and look at the following example:
<?php
$nsqlookupdhosts = getenv('SYMFONY__NSQLOOKUPD__HOSTS');
$nsqdhosts = getenv('SYMFONY__NSQD__HOSTS');
$container->setParameter('nsqlookupd.hosts.parsed', explode(',', $nsqlookupdhosts));
$container->setParameter('nsqd.hosts.parsed', explode(',', $nsqdhosts));
use comma as delimiter in environment variable (you are not restricted to comma, use any)
SYMFONY__NSQLOOKUPD__HOSTS=localhost:4161,some.server:2222
You can use a built-in "json" environment variable processor to decode a JSON string into an array:
SYMFONY__NSQLOOKUPD__HOSTS='["localhost:4161"]'
$nsqlookupdhosts: '%env(json:SYMFONY__NSQLOOKUPD__HOSTS)%'

JMS Serializer ignored my YML Entity exclusions

My config is
jms_serializer:
metadata:
auto_detection: true
directories:
NameOfBundle:
namespace_prefix: ""
path: "#VendorNameOfBundle/Resources/config/serializer"
My YML file named Entity.Project.yml contains
Vendor\NameOfBundle\Entity\Project:
exclusion_policy: ALL
properties:
id:
expose: true
I am loading the serializer like so from within a Controller
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->build();
This completely ignored my YML file and exposes all fields from the Project. I have cleared the cache.
But if I use this instead without the custom subscriber, then the exclusions work
$serializer = $this->get("jms_serializer");
Even explicitly adding a dir does not work either
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->addMetadataDir(realpath($this->get('kernel')->getRootDir()."/../") . '/src/Vendor/NameOfBundle/Resources/config/serializer')
->build();
The docs are not clear on how this path should befined. The above method does not error, but does not pull in the YML files. The below method errors and says the directory does not exist;
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->addMetadataDir('#VendorNameOfBundle/Resources/config/serializer')
->build();
How do I make the JMS Serializer look at my YML file in order to exclude the fields and also use the Subscriber?
As i see from documentation you need to setup your Yaml files:
it is necessary to configure a metadata directory where those files are located:
$serializer =
JMS\Serializer\SerializerBuilder::create()
->addMetadataDir($someDir)
->build();
For more information read manual.
This was helpful Using JMSSerialize to serialize Doctrine2 Entities that follow SimplifiedYamlDriver convention
It would appear that the file names needs to be completely different if you do not specify a namespace. I never thought to specify a namespace as this is not mentioned in the main docs.
If there is no namespace then the addMetaDir usage is fine but you also need to make sure your file names look like this
Vendor.NameOfBundle.Entity.Project.yml

services.yml and path to file in same bundle

How do i refer to a data file in the services.yml, which is located in the same or any bundle?
Im shipping a csv-file which i would like to inject as argument.
It works when i use the direct path:
mybundle.data.csv: %kernel.root_dir%/../vendor/mybundle/my-bundle/mybundle/MyBundle/Resources/data/data.csv
This is pretty verbose and unflexible and thus i am looking for a resolver like:
data.csv: "#MyBundle/Resources/data/data.csv"
But this is not working:
... has a dependency on a non-existent service
"mybundle/resources/data/data.csv".
any ideas?
#-escaping in yaml is supported now
https://github.com/symfony/symfony/issues/4889
You can use it like this
data.csv: "##MyBundle/Resources/data/data.csv"
First of all: in the YAML service and parameter definitions # also refers to another service. That is why your code does not work.
Basically you have two possibilities. The first and simple one is to use a relative path in your bundles services.yml and append it in your CSV class.
For example:
# src/Acme/DemoBundle/Resources/config/services.yml
parameters:
data.csv: "Resources/data/data.csv"
In the class you want to read the CSV file:
// src/Acme/DemoBundle/Import/ReadCsv.php
...
class ReadCsv
{
...
public function __construct($csvFile)
{
$this->csvFile = __DIR__.'/../'.$csvFile;
}
...
}
The other possibility is to make the filename of the CSV file configurable via config.yml (see this article in the Symfony2 cookbook) and insert a special placeholder in the config value that you replace in your AcmeDemoBundleExtension class:
// src/Acme/DemoBundle/DependencyInjection/AcmeDemoBundleExtension.php
...
public function load(array $configs, ContainerBuilder $container)
{
...
$container->setParameter('acme_demo.csv_file', str_replace('__BUNDLE_DIR__', __DIR__'./.., $config['csv_file']);
}
...
Your config.yml would look like:
# app/config/config.yml
acme_demo:
csv_file: __BUNDLE_DIR__/Resources/data/data.csv

Resources