Doctrine MongoDB disable logger on fixture load - symfony

Is there a way to disable Doctrine MongoDB bundle logger on fixture load only? like suggested in https://stackoverflow.com/a/35222045/3965970 but for MongoDB bundle.
Thanks

Yes, absolutely the same way, call
$manager->getConnection()->getConfiguration()->setLoggerCallable(null); right in the fixture.

Unfortunately in Doctrine MongoODM-bundle version 4.0 and higher this approach no longer works.
The easiest way is to disable logging in the configuration, see https://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/config.html
You can also disable the logger manually, but you'll need doctrine_mongodb.odm.command_logger service.
What I did in my Symfony 4 application is to create an alias to enable proper dependency injection and then unregister the listener:
services.yaml
Doctrine\Bundle\MongoDBBundle\APM\PSRCommandLogger:
alias: "doctrine_mongodb.odm.command_logger"
some constructor of a Command or Service:
public function __construct(PSRCommandLogger $mongoLogger)
{
$mongoLogger->unregister();
}

Related

Symfony 4 setter injection not working in bundle with or without autowire

I thought I fully understood Symfony's autowiring behaviour, but there must be something I'm missing and I'm hoping someone can fill in the blanks.
Three questions to begin with:
Is there a difference in the Symfony autowire behaviour in the main application vs in a bundle?
If you ask Symfony to autowire bundle services, does it completely ignore service definitions in the bundles services.yaml file?
Are there known issues with the setter injection functionality in Symfony 4?
I have a service definition within a bundle that uses setter injection but it seems to be completely ignored by Symfony, when I've asked Symfony to autowire my bundle services and even when I've asked Symfony to exclude services from the autowiring.
My application is using Symfony v4.1.3.
I have included my Bundle in my applications bundles.php file.
<?php
return [
//... core bundles,
Acme\\Symfony\\AcmeCustomBundle\\AcmeCustomBundle::class => ['all' => true]
];
In the default Symfony application services.yaml file, I have asked Symfony to autowire my bundles services with the following:
Acme\Symfony\AcmeCustomBundle\:
resource: '../vendor/acme-symfony/custom-bundle/*'
exclude: '../vendor/acme-symfony/custom-bundle/{Model,Tests}'
In my bundles services.yaml file located in ../vendor/acme-symfony/custom-bundle/Resources/config/services.yaml, I have the following:
parameters:
services:
Acme\Symfony\AcmeCustomBundle\Search\ConfigurationReader:
calls:
- method: setIndex
arguments:
$index: '%elasticsearch.index%'
- method: setSchema
arguments:
$schema: '%elasticsearch.schema%'
The parameters are set in my bundle extension class (extends configurable extension), and I have already verified that the parameters do exist and are getting set using the following method:
$container->setParameter('elasticsearch.index', $mergedConfigs['elasticsearch']['index']);
$container->setParameter('elasticsearch.schema', $mergedConfigs['elasticsearch']['schema']);
Now back to the problem. Symfony is not performing the setter injection, even when I tell Symfony to not autowire the above service by doing the following:
Acme\Symfony\AcmeCustomBundle\:
resource: '../vendor/acme-symfony/custom-bundle/*'
exclude: '../vendor/acme-symfony/custom-bundle/{Model,Tests,Search}'
I did however get Symfony to configure my service when I
Turned off the autowiring for the above service
Used the factory method of service creation
This kind of answered my second question above, but I'm not 100% sure of it. Nonetheless, I would much rather not have to use a factory class just to get around what may be an issue with Symfony, or my lack of understanding of how the setter injection / autowiring really work.
Can anyone see something obvious that I'm missing?
You can autowire other methods (e.g. Setters) if you want, just by using the #required annotation in your service:
/**
* #required
*/
public function setFoo(FooInterface $foo)
{
$this->foo = $foo;
}
Autowiring other methods
First, autowiring in bundles that are meant to be shared is not part of the best practices. You must explicitly define the services of a bundle you are going to share, and doing it in xml.
Now, that being said, what autowire does is that wires up the services in the constructor of a class, trying to autoresolve them. It does not perform setter injection.
What you need is to do is to configure an auto-cofiguration entry. In your bundle extension class, you can do:
$container->registerForAutoconfiguration(ConfigurationReader::class)
->addMethodCall('setIndex', ['%elasticsearch.index%'])
->addMethodCall('setSchema', ['%elasticsearch.schema%']);
Now, this is really meant to be used with interfaces or abstract classes, not really with concrete implementations. But is what you can do if you want to autoconfigure your service.
My suggestion is, if you are going to reuse this bundle, to define the services explicitly and in xml. Autowiring is for your business logic and services.

How does Symfony know where to find the services.yml config file?

In Symfony 3.0 (and I'm sure 2.X as well), if I want to make a custom constraint validator with a dependency, I have to register that validator as a service in the dependency injection container (which is described by default in project_directory/app/config/services.yml) using a special tag ( as described here).
This means that the Validator component must know where to look for the service container. This issue also comes up for the ControllerResolver. Since controllers can be registered as services, the ControllerResolver must know where the service container is.
How do the Symfony components know where to look for the service container, and how can I configure this? I ask because I want to build a custom framework using the Symfony components, which means I'll be making my own service container, and I'd like to be able to point the Validator and the ControllerResolver to that service container.
In Symfony Standard Edition, the container is initialised by the Kernel. Have a look at the AppKernel and its parent class.
The kernel loads a configuration file in the registerContainerConfiguration() method:
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');
}
This code will load an environment specific configuration file (config_prod.yml, config_dev.yml etc). In a standard setup that file imports the main config.yml file.
The services.yml file is loaded with an import in config.yml:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
If you're thinking of building your own framework (which is a good learning experience), you'll need to read more code of existing frameworks first. Also, it's worth to read the excellent Create your own PHP framework.
This means that the Validator component must know where to look for the service container
This is scary. I see no connection between validation and the service container. I'd think of a better design.
P.S. Everyone should write their own framework once. The next step should be deleting it.

How to have advanced doctrine configuration in Symfony?

Reading these docs:
Doctrine DBAL Configuration with Symfony
Doctrine ORM Configuration with Symfony
Doctrine Configuration with Symfony
I created this and added to /src/MyBundle/Resources/config/services.php of my generated bundle:
My Configuration code
But I get the error:
[Symfony\Component\DependencyInjection\Exception\LogicException]
Container extension "doctrine" is not registered
What more I missed to do? I probably have to add that I did NOT remove the default orm/dbal settings in /app/config/config.yml.
Also I have no idea what 'read' and what 'host'=>'from_user_config.db' is, I copied/pasted from a third-party application and how to inject db credentials from a separate file into this services.php?

Doctrine DQL with interface

I have an entity in one of my portable bundles with MappedSuperClass set. I extend it to the real entity for each separate project / website and implement the additional properties in a main project bundle. I'm using the interface and the ResolveTargetEntities to relate the entities using the interfaces. This part works flawlessly so far.
The problem comes with my DQL queries, which need to address that particular implemented entity class, the one that extends the mapped superclass. If I use the interface or the mapped superclass, I get the Symfony errors. But I don't want to use my implementation of the superclass name because that would mean I have to change my portable bundle's repository code for each project, which is unacceptable.
One of the ideas was to get the orm mappings from config in the repository and inject the correct class there. But I can't find the way to extract that info from the config file.
The other, better, one was to create some sort of a listener, which would replace the interface with the real thing for me.
So my questions: Does anyone know if this is the usual behavior for the DQL not to be resolved? Does anyone have any ideas how I would implement the code to achieve the resolution by myself?
Any info is appreciated.
I assume you have your concrete class set as a parameter that you are accessing later in your parent bundle. If so you can create a repository for that class (half copied from Sylius).
parameters:
acme.repository.something.class: Doctrine\ORM\EntityRepository
// Or your custom repository
services:
acme.manager.something:
alias: doctrine.orm.entity_manager
acme.metadata.something:
class: Doctrine\ORM\Mapping\ClassMetadata
factory_service: acme.manager.something
factory_method: getClassMetadata
arguments:
- %acme.model.something.class%
acme.repository.something:
class: %acme.repository.something.class%
arguments:
- #acme.manager.something
- #acme.metadata.something
Then you can just call the repository using $this->container->get('acme.repository.something)`
Andrej, I had a similiar situation. You can make a setter method in your custom repository that sets the class name to be used in the dql. You can then have that setter method called with the call tag in your service declaration.
This is what I did in my post repo service declaration. I hope it helps. Good luck.
<service id="cfs_blog.post_repository"
class="%cfs_blog.post_repository.class%"
factory-service="doctrine.orm.entity_manager"
factory-method="getRepository">
<argument>%cfs_blog.post.class%</argument>
<call method="setUserEntityClass">
<argument>%user_class%</argument>
</call>
</service>

Doctrine 2 with Symfony DI Container

I am working on a Zend Framework project that leverages Doctrine 2. I'm trying to get Symfony's DI container working with the project as well, but I'm having trouble. Suppose I have a class "Foo" that needs two instances injected. I can set up a Symfony DI container no problem to fetch me a new "Foo" with the dependencies injected. Great! But now what if I want to make "Foo" a Doctrine entity? All is well when I insert the entity to the DB because I can grab a new one from the DI container and simply persist the entity. However, when I query the entity from the DB Doctrine is instantiating my "Foo" instances and they will not have the proper dependencies injected. How do I get Doctrine to use my DI container so that the entity will have the appropriate dependencies? I know that Doctrine offers a "postLoad" hook in that I could use to inject dependencies on my entity, but that kind of defeats the purpose and benefit of the DI container.
A Doctrine Entity is a newable, not an injectable. Entities are not supposed to be created through a DIC. See this following blog post on the difference between newable and injectable:
http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/
Its a bit tricky, but it can be done. There exists a patch for symfony di container which allows you to pass a already existing object to the container builder instance and let the di container configure it for you (e. g. inject dependencies based on interfaces). The patch is implemented in this repository on github: https://github.com/lstrojny/symfony but didn´t make it upstream to symfony master repository.
You use it like this:
$user = new User();
$container->configure('someId', $user);
Then you could register a PostLoad event handler with Doctrine´s EventManager (see here for more details: http://www.doctrine-project.org/docs/orm/2.0/en/reference/events.html ). In this event handler you configure the loaded entity via the aforementioned method. It´s obvious but you cant use constructor injection in this case, only setter.
This is a bit tricky to set up, but can be very powerful especially in conjunction with the ability of symfony di container to inject dependencies based on interfaces.

Resources