I'm writing a reusable bundle and want to carry out an integration testing of this bundle with the hosting app. The problem is that Symfony doesn't call neither the DependencyInjection\Extension::load nor DependencyInjection\Configuration::getConfigTreeBuilder method of my bundle, therefore the bundle's service configuration is not loaded and the services are not constructed properly. How can I get Symfony to load everything in tests, as if it was a real request?
Here is my test:
namespace Tests\AclAgentBundle;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class IntegrationTest extends KernelTestCase
{
public function testAgentExistence()
{
$container = static::bootKernel()->getContainer();
$service = $container->get('test.Foo\Bar\Baz');
//...assertions...
}
test.Foo\Bar\Baz is an alias of a service, declared in the hosting app's config like
services:
test.Foo\Bar\Baz: '#Foo\Bar\Baz'
used to fetch the service via the container's get method in tests.
I figured out where the problem was. An older cached version of the container was used in the tests, therefore the changes in the configuration of the bundle were not reflected. The command php bin/console cache:clear --env=test helped to rebuild the cache. The methods I mentioned in the original question are not supposed to be called when booting the kernel (at least when the cache already exists).
Related
I am trying to bootstrap a simple microservice type of application with the Symfony router and DI component, but without using the symfony/skeleton package as a starting point.
Routing works, but DI does not. Looking at the documentation of the service container, it is unclear of how my app would actually use the config/services.yaml file. I don't understand when is it loaded, how it is loaded, and how I would tell Symfony to use a services.php instead?
Similarly, if I install new composer packages, they dump some .yaml config files in config/packages/. Would I then have to load these files manually, using the config component?
All this typically happens (in a Symfony application) in the application Kernel, which is instantiated and booted by the "front-controller" script (e.g. public/index.php).
In the default Symfony 5 provided kernel you'll find this:
protected function configureContainer(ContainerConfigurator $container): void
{
$container->import('../config/{packages}/*.yaml');
$container->import('../config/{packages}/'.$this->environment.'/*.yaml');
if (is_file(\dirname(__DIR__).'/config/services.yaml')) {
$container->import('../config/services.yaml');
$container->import('../config/{services}_'.$this->environment.'.yaml');
} elseif (is_file($path = \dirname(__DIR__).'/config/services.php')) {
(require $path)($container->withPath($path), $this);
}
}
This loads all *.yaml files in the config/packages directories, and load the file config/services.php but only if the file config/services.yaml does not exist.
If you are building your own application without using the framework, you'll have to load these files wherever it makes sense for you application.
With the reduced footprint of Symfony 5, building your own is likely not particularly cost-effective, you can use the minimal symfony/skeleton and you would already have a very "micro" starting point, without having to spend time deciding these things.
If you are set on "building your own", either because of specific requirements or as a learning exercise, I recommend you reading this part of the documentation: Create your own PHP Framework. It's a very useful way of learning how the many pieces fit together.
So I've created a new Symfony 4.1 project last week and have been working on it without any issues. Until yesterday when I started using some third party dependencies. I installed the Guzzle Client by using the composer require guzzlehttp/guzzle. This installed the package just fine as far as I can see.
However, when I then try to inject this service into a constructor in my code (an event or command), I get the error message: Cannot autowire service "App\xxx\Command\testCommand": argument "$client" of method "__construct()" references class "GuzzleHttp\Client" but no such service exists.
The Guzzle service is not the only one that doesn't work, this is just an example. The weird thing to me is that there are a lot of dependencies that I have that work just fine like the Doctrine DocumentManager, JMS serializer and NelmioApiDocBundle.
What I've tried so far (and actually semi-solves the problem) is adding the service name to my services.yaml file:
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.
GuzzleHttp\Client: <-- Adding this makes it work flawlessly.
The problem I have with this solution is that I would end up adding hundreds of services to my services.yaml file just to get them to work. It seems to me like this is not the optimal solution.
So my question is how I could resolve this in the most elegant way. Is this something I'm doing wrong in my project? Is it something that has to be resolved on the vendors side? And if so, how could it be made to work with Symfony 4.1?
composer require GuzzleHttp\Client
It seems like you are trying to require a class, not a package. I was not aware composer is smart enough to recognize a package by it's provided classes, so I checked... it can't, only gives package name suggestions.
Make sure you understand what is a composer package.
The Guzzle service is not the only one that doesn't work, this is just an example.
You don't have a Guzzle service. You only have the guzzle library with it's classes. Services require a service definition, like the one you wrote.
The other services like Doctrine you mentioned are working, because they are bundles, which provide service definitions.
Either you use a bundle, or you use a library, and hook it up yourself.
In the case of guzzle, I would not use a bundle. Then again, I would not use guzzle directly anymore, but HTTPlug. (bundle: https://github.com/php-http/HttplugBundle, library: https://github.com/php-http/httplug)
In my case, I just needed to re-generate the optimized autoload files.
So using composer dump-autoload solved the issue for me.
We have a project we're migrating to Symfony 3.3 and the new DI configuration. We have a service that used to be passed values from parameters file based on an argument supplied to the command. (bit more complex than this but you get the idea):
$this->service->setOptions($this->getContainer()->getParameter('account_'.$accountId));
With the migration we want to stop making the commands ContainerAwareCommand as we can now use new DI for the services. However, this leaves us stuck working out an alternative method of fetching the parameters when we cannot statically configure them in services.yml
Would it be best for me to just use the Yaml component and read the parameters.yml file in manually for this or is there some other, more appropriate solution?
This question already has an answer here:
How to use DependencyInjection from symfony in stand alone application with commands?
(1 answer)
Closed last year.
A Symfony novice here. After reading some of the Symfony documentation and some answers here at SO, I am now almost completely confused.
I am trying to use the console application component and create a small db-aware console application.
Many people state that in order to use Symfony's DI features it would be enough to inherit my command class not from Symfony\Component\Console\Command\Command but from ContainerAwareCommand.
However when I try this I get a Method Not Found error on an application::getKernel() call.
I have a feeling that DI features are in fact not available in a console application based on the console component. Is there another kind of Symfony console application, for example, based on the full-blown framework?
I very much like the simple framework provided by the console component Symfony\Component\Console\Application. But the question is then - what to do for dependency injection and DBAL? All examples that I find seem to refer to the full Symfony framework and get me just all the more stuck.
Just a quick update on my progress if anybody stumbles upon the same problems.
I incorporated into my project the PHP-DI dependency injection framework, which seems to be working fairly well with no configuration (so far) - it figures out quite a lot by reflection, actually.
The same way, Doctrine\DBAL is included as a standalone library (I opted against the O/RM part of it, as it is really a tiny project and I'm on a much firmer ground with SQL than anything else) and the connection is simply returned by a connection provider which is injected wherever needed by the DI.
One thing I couldn't figure out is how to have the command classes instantiated by the DI library without my help, so I actually had to inject the container itself into my overridden application class and override the getDefaultCommands() where I then pull the instances out of the container manually. Not ideal but will have to do for now.
If your command extends ContainerAwareCommand
...
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
...
class MyCommand extends ContainerAwareCommand
{
The DI container is available with the getContainer() method. (like in a standard controller), ex:
$this->validator = $this->getContainer()->get('validator');
I don't know if your question is still relevant, but I have an answer as I stumbled across the same problem here.
You just have to create the kernel yourself and give it to the \Symfony\Bundle\FrameworkBundle\Console\Application that extends the basic \Symfony\Component\Console\Application.
<?php
// CronRun.php
require __DIR__.'/../../../../vendor/autoload.php';
require_once __DIR__.'/../../../../app/AppKernel.php';
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
$application->add(new \KingdomHall\TaskBundle\Command\CronCommand());
$input = new \Symfony\Component\Console\Input\StringInput('k:c:r');
$application->run($input);
You could use a solution I just pushed it to packagist.org. Includes full working symfony/dependency-injection. You're welcome to give it a shot. use composer to create your own project composer create-project coral-media/crune project_dir or just clone the repository.
https://packagist.org/packages/coral-media/crune
You only need to install DBAL dependencies (I don't suggest ORM if you don't really need it). Configure connection parameters in .env and just define a service to handle connection. That service can be injected in your Commands using public setMyService($myService) method with #required annotation. Also you could create a Connection class and bind is as parameter in your command constructor.The crune boilerplate also supports autowire and autoconfiguring features.
I've seen this How can i inject dependencies to Symfony Console commands? but that answer doesn't really give enough information and is already explained here http://symfony.com/doc/current/cookbook/console/console_command.html
The problem is a containerAwareCommand doesn't work with the setup here http://symfony.com/doc/current/components/console/introduction.html
In order to use containerAwareCommand from what I can tell, I need my application to use
Symfony\Bundle\FrameworkBundle\Console\Application
instead of
Symfony\Component\Console\Application
But using the frameworkBundle Application class requires an instance of KernelInterface and won't allow me to pass in a name and version to my application.
Here is what I have that won't work with containerAwareCommands
#!/usr/bin/env php
<?php
require __DIR__.'/../src/vendor/autoload.php';
$app = new Symfony\Component\Console\Application('spud', '0.0.1');
$app->add(new Isimmons\Spudster\Console\Commands\SayHelloCommand);
$app->run();
The command it's self runs but I get an error when trying to use getContainer
Call to undefined method Symfony\Component\Console\Application::getKernel()
On a related topic which will probably come up next, The documentation for registering a class in the container shows using a app/config/config.php file. But I don't have an app directory since this is not a full symfony application. My base directory in which all of the app except for the file above is located, is src/lib. If I can figure out the first part above, will symfony be able to find the config file at src/lib/config/config.php?
You can use Consolefull application.
Consolefull is a simple library to work with Symfony Console Component and Symfony Dependency Injection Component