I've defined a service in the Resources/config/services.yml:
services:
gSm.gate.terminal:
class: Stream\TerminalBundle\StreamTerminal
arguments: [ [], [%terminal_login%, %terminal_password%] ]
And I try to access in inside my controller action:
public function displayAction() {
$terminal = $this->get('gSm.gate.terminal');
return $this->render('StreamTerminalBundle::display.html.twig');
}
Server returns following exception: 500 You have requested a non-existent service "gsm.gate.terminal". The bundle is registered in the appKernel, the bundle uses .yml config files... and I don't know what else can I look at to make this service available... thanks for any help.
EDIT:
My extension class code is:
namespace Stream\TerminalBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class StreamTerminalExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
Make sure you're importing the bundle's services.yml. A simplest way is to import it from config.yml. A better and more advanced solution is to write an extension.
To see an example of an extension class, see my bundle's one. If you want to use YAML, just change services.xml to services.yml and XmlFileLoader to YamlFileLoader.
Is your services.yml file being parsed by the Extension class?
By default in a new bundle it's setup to load the xml file.
Can you paste your
Acme\Bundle\YourBundle\DependencyInjection\AcmeYourExtension
class?
I'm not sure you can use uppercase letters in you service names. The exception gives such a hint "gsm.gate.terminal" while your service is "gSm.gate.terminal".
Related
I want to build a private ecosystem with multiple reusable bundles, similar to the Sonata project. This is my first time so I followed Symfony2 - creating own vendor bundle - project and git strategy and set up a simple bundle named PUIEconomyBundle with a DefaultController. I imported the bundle into an example project from my Git repo using composer.json.
Now i'm running into a 404 No route found for "GET /test". It's important to have annotated routes to keep an overview. How do I introduce working annotated routing into my controllers? The debug:router does not mention the route from this bundle, although the profiler says the PUIEconomyBundle is enabled.
DefaultController:
class DefaultController extends Controller
{
/**
* #Route("/test", name="homepage")
* #param Request $request
*
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
dump('Hello!');die;
}
}
Extension:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
//$config = $this->processConfiguration($configuration, $configs);
$fileLocator = new FileLocator(__DIR__.'/../Resources/config');
$loader = new Loader\YamlFileLoader($container, $fileLocator);
$loader->load('services.yml');
}
Services.yml:
services:
pui_economy.routing_loader:
class: Company\PUI\EconomyBundle\Service\RoutingLoader
tags:
- { name: routing.loader }
RoutingLoader:
class RoutingLoader extends Loader
{
public function load($resource, $type = null)
{
$collection = new RouteCollection();
$resource = '#PUIEconomyBundle/Resources/config/routing.yml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$collection->addCollection($importedRoutes);
return $collection;
}
public function supports($resource, $type = null)
{
return 'advanced_extra' === $type; // ??
}
}
Routing.yml:
pui_economy:
resource: "#PUIEconomyBundle/Controller"
type: annotation
Thank you
It seems that you forget to add this:
app_extra:
resource: .
type: extra
in app/config/routing.yml.
See Using the custom Loader.
Why are u using a custom routing loader? This is a pretty adavanced topic which is not neccessary to simply bind a controller on a route via annotations.
You can find a working example for the #Route annotation here: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html
You should also remove the die() statement. Symfony mabey wouldn't give you a response if you kill the request in this way.
You don't need custom route loader to load your bundle annotated routes.
I was facing similar issue. All we need is to put this configuration in application where we want to load bundle
config/routes.yaml
my_cool_bundle_routes:
# loads routes from the given routing file stored in some bundle
resource: '#XyzAuthBundle/Controller/'
type: annotation
and that's all :)
my_cool_bundle_routes naming is not significant. It just have to be unique.
I have two services and I want pass my parameter from config.yml
my config.yml
parameters:
MyService.class: Acme\UserBundle\Services\sendEmail
MyService.arguments: #mailer
NewUserListener.class: Acme\UserBundle\Event\NewUserListener
NewUserListener.arguments: #MyService
my service.yml inside bundle
services:
MyService:
class: %MyService.class%
arguments: [%MyService.arguments%]
NewUserListener:
class: %NewUserListener.class%
arguments: [%NewUserListener.arguments%]
tags:
- { name: kernel.event_listener, event: new.user, method: sendEmailToUsers }
I got an error
You cannot dump a container with parameters that contain references to
other services
My Questions are:
How can I inject my arguments from config.yml?
Where can i Find the list of "global service" like #mailer ? i don't find in doc
You can't reference a service in a parameter. You should replace %MyService.arguments% with #mailer.
To find all available services, run php app/console container:debug
This a bit more complicated!
First, you have to declare your default services like that (I changed all the names in order to be compliant with the Symfony2's conventions):
# resources/config/services.yml
services:
my_own.service.default.class: Acme\UserBundle\Services\sendEmail
my_own.user_listener.default.class: Acme\UserBundle\Event\NewUserListener
services:
my_own.service.default:
class: %my_own.service.default.class%
arguments: [#mailer]
my_own.user_listener:
class: %my_own.user_listener.class%
arguments: [#my_own.service]
tags:
- { name: kernel.event_listener, event: new.user, method: sendEmailToUsers }
We will define some configuration for your bundle in order to allow to change the used services:
namespace My\OwnBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {#inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('my_own');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
$rootNode
->children()
->scalarNode('service')->defaultValue('my_own.service.default')->end()
->scalarNode('user_listener')->defaultValue('my_own.user_listener.default')->end()
->end();
return $treeBuilder;
}
}
Note that, by default, we use our default services defined above in our bundle.
You now can use the following to change your services (in your app/config.yml) for instance:
# app/config.yml
my_own:
service: my_other.service
user_listener: my_other.user_listener
Of course, you can define the services my_other.service and my_other.user_listener as you want in your bundle or in another bundle.
Now we have to tell how to use this configuration to take the wanted services:
namespace My\OwnBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class MyOwnExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$container->setAlias('my_own.service', $config['service']);
$container->setAlias('my_own.user_listener', $config['user_listener']);
}
}
Finally, in the rest of your code you have to use the aliased services my_own.service and my_own.user_listener in your code:
// In one of your controller:
$this->container->get('my_own.service');
/* or directly */ $this->get('my_own.service'); // if your controller is a child of the framework bundle class `Controller`.
I'm getting the following error after following and adapting this
ParameterNotFoundException: You have requested a non-existent parameter "mynamespace_admin.amazon_s3.aws_key". Did you mean this: "mynamespace_admin.amazon_s3.class"?
In config.yml I have:
mynamespace_admin:
amazon_s3:
aws_key: %amazon_aws_key%
aws_secret_key: %amazon_aws_secret_key%
base_url: %amazon_s3_base_url%
And in my parameters.yml I have:
amazon_aws_key: ###
amazon_aws_secret_key: ###
amazon_s3_base_url: ###
amazon_s3_bucket_name: ###
And in services.yml:
parameters:
mynamespace_admin.amazon_s3.class: AmazonS3
mynamespace_admin.image_uploader.class: mynamespace\Bundle\AdminBundle\Uploader\ImageUploader
mynamespace_admin:
amazon_s3:
class: %mynamespace_admin.amazon_s3.class%
arguments:
- "%mynamespace_admin.amazon_s3.aws_key%"
- "%mynamespace_admin.amazon_s3.aws_secret_key%"
image_uploader:
class: mynamespace_admin.image_uploader.class%
arguments: [image_storage_filesystem]
Can anyone see what I have configured incorrectly or advise on how to debug this? Why can't mynamespace_admin.amazon_s3.aws_key be read from config.yml?
If things are not listed under the parameters key in the service configuration, it is not assumed it is a parameter.
In all other cases (except from the services key), it is assumed that it is the configuration for an extension. In this case, the extension called mynamespace_admin. That extension should parse the settings and maybe put them in the container as parameters, or use them to decide which files to include, etc.
Assume you have a correct Configuration class for the config you have given, your extension will look like this if you want to use the config as parameters:
// ...
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class MynamespaceAdminExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// ... do some other stuff, like loading service definition files
// loop through the processed config and save them as parameters
foreach ($config['amazon_s3'] as $name => $value) {
$container->setParameter('mynamespace_admin.amazon_s3.'.$name, $value);
}
}
}
I'm trying to create a model class (which will use DBAL), and i'd like to use it like a service in my bundle.
I've tried to create a service with this configuration in my bundle :
services:
X:
class: X
arguments: [#database_connection]
But the fact is i don't want to configure this service in app/config/config.yml because it will only be used in one bundle.
Is there any way to create a specific bundle service, and giving #database_connection parameter to the class ? Or am i forced to configure it for all my app ?
My goal here is only to have distinct class for my controller and my model, without using the Doctrine ORM/Entity, just the DBAL.
Yes, every bundle has his own config files.
# src/Acme/YourBundle/Resources/config/services.yml
services:
X:
class: X
arguments: [#database_connection]
The bundle configuration is loaded trough the DIC. So this file in your bundle is important
// src/Acme/YourBundle/DependencyInjection/AcmeYourBundleExtension.php
namespace Acme\YourBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class AcmeYourExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
Generally, you should configure all services in the bundle specific services.yml and not in config.yml. So you can reuse them. But the service is visible for the complete application not only for the bundle. But this should be no problem.
I wrote custom Twig loader that fetch templates from database and it works in Twig "standalone" library.
Now i want to use that in Symfony2 but can't find where to change Twig loader via Symfony2 settings.
Thx in advance for any tips on that
Register your own twig loader + tell Twig_Loader_Chain to try loading with your loader at first. You can create and add as many loaders to your Twig_Loader_Chain as you want.
services:
Acme.corebundle.twig.loader.filesystem:
class: Acme\CoreBundle\Twig\Loader\Filesystem
tags:
- { name: templating.loader }
Acme.corebundle.twig_chain_loader:
class: Twig_Loader_Chain
calls:
- [ addLoader, [#Acme.corebundle.twig.loader.filesystem] ]
- [ addLoader, [#twig.loader] ]
Now you should create your loader. Twig loaders have to implement Twig_LoaderInterface.
Acme/CoreBundle/Twig/Loader/Filesystem.php
PSEUDOCODE:
namespace Acme\CoreBundle\Twig\Loader;
use Twig_LoaderInterface;
class Filesystem implements Twig_LoaderInterface {
/**
* {#inheritdoc}
*/
public function getSource($name)
{
//code...
}
/**
* {#inheritdoc}
*/
protected function findTemplate($name)
{
//code...
}
/**
* {#inheritdoc}
*/
public function isFresh($template, $time)
{
//code...
}
//...
}
Now we have defined our services and created a new loader.
Problem is that Twig doesn't know about our new Twig_Loader and still uses its own -default- "twig.loader".
To check run on CLI:
app/console container:debug twig.loader
In order to modify services outside of your own bundle you have to use CompilerPasses.
Create our own that assigns your loader service to the twig environment:
Acme/CoreBundle/DependencyInjection/Compiler/TwigFileLoaderPass.php
<?php
namespace Acme\CoreBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class TwigFileLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('twig');
$definition->addMethodCall('setLoader', array(new Reference('Acme.corebundle.twig_chain_loader')));
}
}
There is the "addMethodCall" call which does nothing more than defining a setter injection as in the service definitions. The difference is that in a compiler pass you can access every service, not only your own ones. As you can see the chain loader has been defined as the new loader for the twig environment.
To Accomplish this task you have to tell Symfony that it should use this compiler pass. Compiler passes can be added in your bundle class:
Acme/CoreBundle/AcmeCoreBundle.php
<?php
namespace Acme\CoreBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Acme\CoreBundle\DependencyInjection\Compiler\TwigFileLoaderPass;
class AcmeCoreBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new TwigFileLoaderPass());
}
}
If the corresponding file does not exist your new Twig_Loader_Filesystem throws an error and the chain loader continues with default twig loader as fallback.
Have a look at this page at GitHub. Specially <parameter key="twig.loader.class">Symfony\Bundle\TwigBundle\Loader\Loader</parameter>
You can configure this key in your config.yml
To overwrite the key in your config.yml you need to do it under services not twig as it's not support in the configuration parser at the moment (2.0.9)
twig:
cache:...
debug:...
...
services:
twig.loader:
class: Acme\CoreBundle\Twig\Loader\FilesystemLoader