bundle's service (autowire) not available in controller's method - symfony

I'd like to use https://github.com/tedious/TedivmStashBundle bundle in my symfony 4 project. Added by composer, configured in /config/stash.yaml file and according to the profiler bar, it's working basically.
Now, I want to use to caching values in my controller. I've try to add the service stash by its name to the method's parameter, using the same name as the example says:
$pool = $this->container->get('stash');
but the framework did not find the service. It can't be added like the example explained neither.
How can I use this bundle as a service (autowired) in my symfony 4 app?
UPDATE
services.yaml:
stash.default_cache:
class: Tedivm\StashBundle\Service\CacheService
public: true
arguments:
$name: 'stash'
Tedivm\StashBundle\Service\CacheService: '#stash.default_cache'
Controller:
public function something(Request $request, CacheService $service, ...
It's looks like working now :)
Thanx for the suggestion (and the correct solution later) to #Cerad

Here is an example of how to approach this sort of problem when dealing with bundles that are not quite ready for autowire.
Start by installing a test project:
symfony new --full stash --version=lts
composer require tedivm/stash-bundle
Note that the bundle does not directly support Symfony 5 hence the lts. Note also that the bundles does not support Flex so you have to add your own config/packages/stash.yaml file per the bundle's readme file.
# config/packages/stash.yaml
stash:
drivers: [ FileSystem ]
FileSystem: ~
At this point we can determine which service we need to inject:
bin/console debug:container stash
Information for Service "stash.default_cache"
=============================================
Class CacheService
---------------- -----------------------------------------
Option Value
---------------- -----------------------------------------
Service ID stash.default_cache
Class Tedivm\StashBundle\Service\CacheService
Most of the time you would like to use an interface for injection but a peek at the source code reveals that the bundle does not use interfaces. As a side note, calling a third party service 'stash' is not a good idea. It really should have been 'tedivm.stash' but I digress.
We can now create an alias and then typehint against it:
# config/services.yaml
Tedivm\StashBundle\Service\CacheService : '#stash' # alias for typehinting
# Controller class
public function index(CacheService $cacheService)
{
return new Response('Cache ' . get_class($cacheService));
}
And that should do it.

Related

Definine a synthetic service in Symfony 4.1 doesn't work from within a bundle

I am creating a Symfony Bundle that defines a synthetic service:
my_alias:
public: true
synthetic: true
After installing the bundle, the application's console command cache:clear always fails with the error:
[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Invalid service "my_alias": class "" does not exist.
If I create the same alias inside the actual framework, this error does not turn up.
Is this a bug, or am I doing something wrong?
When you clear the cache the kernel is recompiled and the container will not figure out your service that is not injected to the container, that's why you need to inject it under your Kernel class:
if (!$container->hasDefinition('my_alias')) {
$container->set('my_alias', new SyntheticService());
}

symfony4 migrate autowire to true - get error message

I am migrating from symofony 2.7 to symfony 4.0. With success I migrated one bundle. Now I am migrating the second bundle and the error message is coming up. I don't get at all what symfony 4.0 wants from me.
If I turn on autowire: true this error message is coming up.
Cannot autowire service "App\Kernel": argument "$environment" of method "Symfony\Component\HttpKernel\Kernel::__construct()" must have a type-hint or be given a value explicitly.
Can somebody help me?
If I turn it off, no message is coming up.
Update
I registered my bundle only in bundles.php
App\Backend\AccountBundle\BackendAccountBundle::class => ['all' => true],
Usually the Kernel is added to the Service Container as a so called synthetic service, meaning it's not generated by the DI-container from configuration. Rather the id is set and then the previously configured service is just added to the container. It seems rather odd that your bundle's container wants to create a new kernel here. So I would check where and how you want to access the kernel in any of the bundle's services and whether you actually want to pass in the kernel and not something else. If you do you might want to check the Service Container-documentation on synthetic services.
As to the error itself. Symfony's autowiring often falls flat when you have services that require parameters like with the Kernel:
public function __construct(string $environment, bool $debug) {...}
In these cases you have to either have a parameter defined in your services.yaml that matches the name of the parameter:
# config/services.yaml
parameters:
environment: prod
debug: false
or you have to tell the configuration which parameters you want to have in those places.
App\Kernel:
$environment: prod
$debug: false
This will tell the autowiring that only the 2 arguments named environment and debug should be overwritten with the values you provide, but the rest is done via autowiring. This way you can skip the arguments: part of the definition and you can also skip all arguments you know are correctly set via autowiring.
For example if you have a service like this:
class MyService {
public function __construct(OtherServce $service, string $someParameter) {}
}
# config/services.yaml
services:
_defaults:
autowiring: true
MyService:
$someParameter: 'someValue'
This is the same as explicitly writing:
services:
MyService:
class: MyService
arguments:
- '#OtherServce'
- 'someValue'

Symfony Bundle references "#Bundle" - FileLocatorFileNotFoundException

I am currently building my own Symfony bundle (I am using version 3.3). It works fine so far, but now I wanted to move the bundle-specific service definition out of my app/config/services.yml to a service definition within the bundle.
I created a src/MyBundle/Resources/config/services.yml and followed the guide How to Load Service Configuration inside a Bundle to load it. That works just fine, but I thought that the paths inside the newly created services.yml look a bit ugly:
MyBundle\:
resource: '../../*'
exclude: '../../{Tests}'
So I thought it would look a little cleaner, if I use the #Bundle-notation instead:
MyBundle\:
resource: '#MyBundle/*'
exclude: '#MyBundle/{Tests}'
However, than I was facing the error:
FileLocatorFileNotFoundException
The file "#MyBundle" does not exist (in: <...>\MyBundle\DependencyInjection/../Resources/config), where <...> corresponds to an absolute path.
I also tested whether it was a naming issue of the bundle name, but when I have a wrongly named bundle reference in the app/config/services.yml, e.g. #SomeBundleThatDoesNotExist then I get a different error:
FileLoaderLoadException
Bundle "SomeBundleThatDoesNotExist" does not exist or it is not enabled.
So my question is: Why does the #Bundle-notation work fine in the app/config/services.yml but not in the src/MyBundle/Resources/config/services.yml?
If you use the service remember make public for external use:
autowire: true
public: true
But, if your symfony dont know where is the bundle please chec this answer:
Symfony generated Bundle doesn't work

SYMFONY3 in prod look for TWIG template in wrong folder instead of custom bundle indicated in routing.yml and AppKernel.php

I am implementing a SYMFONY 3 project in production mode for the first time.
I follow OceanDigital tutorial and the standard doc.
I went thru a bunch of issues linked to user writing rights that I've solved, and I do get now a SYMFONY ERROR page (one step closer to victory) with this message:
Unable to find template "MyBundle:std_page:home.html.twig" (looked
into: /[folder to symf project]/app/Resources/views,
/[folder to symf project]/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form,
/[folder to symf project]/vendor/knplabs/knp-menu/src/Knp/Menu/Resources/views).
If I look in my [my symf project]\app\config\routing.yml, I have:
my_bundle:
resource: "#MyBundle/Resources/config/routing.yml"
options:
expose: true
In [my symf project]\app\AppKernel.php, in the registerBundles() function, I have:
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = [
....
new MyBundle\MyBundle(),
.....
]
}
}
And the file regarding the template that should be fetched [my symf project]\src\MyBundle\Ressources\views/std_page/home.html.twig exists.
What did I not set up right, in production mode, to have it looking for the TWIG template in the folder [my symf project]\src\MyBundle\Ressources\views/?
After some search it happens to be a mistake similar to the one described in that SO thread.
In my controller I had:
return $this->render('MyBundle:Std_page:home.html.twig',$parameters);
Instead of:
return $this->render('MyBundle:std_page:home.html.twig',$parameters);
The development was made on a WINDOWS 10 OS, and it is set up in production on a UBUNTU 16.04. It seems that UBUNTU is stricter than WINDOWS regarding the letter case.

Unit testing a symfony service class with phpunit

I have a basic class GenericHelper.php in directory Foo/BarBundle/Helper
I registered it as a service in Foo/BarBundle/Resources/config/services.yml:
parameters:
generic_helper.class: Foo\BarBundle\Helper\GenericHelper
services:
generic_helper:
class: %generic_helper.class%
and I'm able to access it in a command by doing the following:
$helper = $this->getContainer()->get('generic_helper');
Now, I'd like to unit test that class with PHPUnit; I have the following code (similar to http://symfony.com/doc/2.0/book/testing.html#unit-tests):
namespace Foo\BarBundle\Tests\Helper;
use Foo\BarBundle\Helper\GenericHelper;
class GenericHelperTest extends \PHPUnit_Framework_TestCase {
public function testSomeMethod() {
$helper = new GenericHelper(); //line 10
$this->assertEquals($helper->someMethod(), SOME_RESULT);
}
}
Running PHPUnit results in the following error:
PHP Fatal error: Class 'Foo\BarBundle\Helper\GenericHelper' not found in /DIR/src/Foo/BarBundle/Tests/Helper/GenericHelperTest.php on line 10
Grepping for 'GenericHelper' only yields a few results:
the Class itself and the Test class
the services.yml file
appDevDebugProjectContainer files in app/cache/dev/, which have all the service getters
Question(s):
Does Symfony prevent PHPUnit from directly constructing a service class?
Is there a way to do this without creating a Symfony Container then accessing the service (as done here: Access Symfony 2 container via Unit test?)? I mean, it's still just a basic class...
Running phpunit with the -c flag pointing to the directory containing the phpunit.xml.dist file solved the issue. Doing this includes bootstrap.php.cache and therefore the autoloading stuff necessary.

Resources