I'm trying to get some params from a config yaml file, I'm doing the following:
namespace ExampleVendor\AdminBundle\Controller;
/**
* #Route("/", name="_home")
*/
public function index(ParameterBagInterface $params) {
$menuIcons = $this->getParameter('admin-aside-menu');
dump($menuIcons);
return $this->render('#ExampleVendorAdminBundle/index.html.twig');
}
As you can see is just a dump function that prints the admin-aside-menu parameter, the problem is, this code works when it is in a controller in App\src\Controller, I mean in the "main" application src folder, but when I copy this code and paste it in a controller inside a custom bundle (something like vendor/myVendor/myBundle/src/Controller) I get an error when reloading the page:
Could not resolve argument $params of "ExampleVendor\AdminBundle\Controller\AdminController::index()", maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"?
I need ParameterBagInterface to get those parameters but I dont know how to "inject" it in my controller.
Okay so it seems like this post says how to fix this issue but, since the controller I need to inject something is in a custom bundle in the vendor folder, the answer doesn't help me at all
Project
|
+--AdminBundle <- My custom bundle, installed via composer as symlink
| |
| +--Controller
| | |
| | +--AdminController.php <- Heres where I want to get my parameter
| |
| +--DependencyInjection
| +--Entity
| +--Resources
| |
| +--config
| |
| +--custom.yaml <- There are my custom params
| +--services.yaml <- Heres where I should register my controller as service?
|
+--assets
|
+--bin
|
+--config
|
+--public
|
+--src
|
+--templates
|
+--vendor <- Heres my admin bundle as symlink
The AdminBundle folder is installer via composer as symlink so I can use it in my project, so knowing this, anyone know how can I inject ParametersBag or the parameter directly into my controller?
This is my custom.yaml that holds the params from my bundle
// AdminBundle/Resources/config/custom.yaml
parameters:
admin-aside-menu:
items:
- icon: 'Home/Chair2'
title: 'Prueba'
- icon: 'Home/Deer'
title: 'Prueba Venado'
This is the configuration class in the dependency injection
// AdminBundle/DependencyInjection/Configuration.php
class Configuration implements ConfigurationInterface {
public function getConfigTreeBuilder() {
$treeBuilder = new TreeBuilder('admin-aside-menu');
$treeBuilder->getRootNode()
->children()
->arrayNode('items')
->children()
->scalarNode('icon')->end()
->scalarNode('title')->end()
->end()
->end()
;
return $treeBuilder;
}
}
and this is the bundle extension
// AdminBundle/DependencyInjection/ExampleVendorAdminExtension .php
class ExampleVendorAdminExtension extends Extension {
/**
* Loads a specific configuration.
*
* #throws \InvalidArgumentException When provided tag is not defined in this extension
*/
public function load(array $configs, ContainerBuilder $container) {
$loader = new YamlFileLoader( $container, new FileLocator(__DIR__.'/../Resources/config') );
$loader->load('custom.yaml');
$loader->load('services.yaml');
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
}
}
I wrote this in my services.yaml to inject my parameter into my controller according to Florian's response
// AdminBundle/Resources/config/services.yaml
services:
admin-bundle.controller.admin-controller:
class: ExampleVendor\AdminBundle\Controller\AdminController
arguments:
- "%admin-aside-menu%"
it seems to work in a way because when I write "%admin-aside-menu%" bad (a typo) an error page says
"Did you mean this: "admin-aside-menu"?"
So I think it is actually loading my param but in my controller I cannot "inject" it
This is the controller:
/**
* #Route(name="admin")
*
* Class AdminController
* #package ExampleVendor\AdminBundle\Controller
*/
class AdminController extends AbstractController {
public function __construct(array $adminAsideMenu) {
dump($adminAsideMenu);
}
}
but when I run this I get this error
The controller for URI "/admin/" is not callable. Controller "ExampleVendor\AdminBundle\Controller\AdminController" has required constructor arguments and does not exist in the container. Did you forget to define the controller as a service?
Building your own bundle is not straightforward.
From the bundle structure you shared with us, I can see that you already have the Dependency injection folder.
This folder should contains two file:
Configuration.php
YourBundleNameExtension.php
The parameter you want to inject in your controller should be in the configuration of your bundle so you have to complete the Configuration.php to add it. (I'm not a pro for that so I let you search by yourself)
Moreover, in order to acces your configuration in your bundle's code, you have to inject the configuration as a parameter with a a prefix for your bundle. You can find an example to see how to do it here : https://github.com/Orbitale/CmsBundle/blob/4.x/DependencyInjection/OrbitaleCmsExtension.php
Now in your Resources/services.yaml you can add you controller as a service:
services:
bundle_name.controller.your_controller:
class: Your\Controller\Namespace
arguments:
- '%bundle_key.your_parameter_name%'
Something like that should work but it's maybe not totally clear. So if you have more questions, I'll try to answer you.
Don't hesitate to check existing bundles as a source of inspiration.
---------------------- EDIT TO ANSWER EDIT 2 ----------------------
From what I see, your bundle configuration key is "admin-aside-menu"? Maybe it should be "admin_aside_menu" instead (to match convention). Anyway :
Yeah you almost there but there is missing something. You can't directly define parameters in config from your bundle. Instead, when you will use your bundle from your application you will have a file like this in /config/packages/admin-aside-menu.yaml :
admin-aside-menu:
items:
- icon: 'Home/Chair2'
title: 'Prueba'
- icon: 'Home/Deer'
title: 'Prueba Venado'
This is your bundle configuration for your current usage and this should match the format you define in your AdminBundle/DependencyInjection/Configuration.php file. (I can't help you with that because it's not something I often do).
You can now remove totally the file AdminBundle/Resources/config/custom.yaml because the configuration is in your application.
Then in your extension, you can get this configuration to inject it in your the application parameters with a prefix for your bundle. If I modify your code, it should be something like this :
// AdminBundle/DependencyInjection/ExampleVendorAdminExtension .php
class ExampleVendorAdminExtension extends Extension {
/**
* Loads a specific configuration.
*
* #throws \InvalidArgumentException When provided tag is not defined in this extension
*/
public function load(array $configs, ContainerBuilder $container) {
$loader = new YamlFileLoader( $container, new FileLocator(__DIR__.'/../Resources/config') );
// $loader->load('custom.yaml'); not needed anymore
$loader->load('services.yaml');
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// This part inject your config in parameters
foreach ($config as $key => $value) {
$container->setParameter('admin-aside-menu.'.$key, $value);
}
}
}
Now your config parameters are set!
Last step, inject it in your controller :
// AdminBundle/Resources/config/services.yaml
services:
admin-bundle.controller.admin-controller:
class: ExampleVendor\AdminBundle\Controller\AdminController
arguments:
- "%admin-aside-menu.items%"
Is this ok ?
Open service.yaml
then put the class name and choose an id to fetch it with autowiring
services:
ExampleVendor\AdminBundle\Controller = '#adminbundle_thenameyouchoose'
You should never write code in vendor packages due to any coding standards and practices. vendor is not a folder that you should push into the git repository, one of the reasons is the excess size of the static libraries that does not practically belong to your project, also this practice would make a total mess when you would try to update your composer dependencies. You should keep it clean and add vendor into the .gitignore. Symfony provides good support for overriding services check this.
Related
I am developing a Symfony 3 application. Symfony profiler logs tell me:
Relying on service auto-registration for type "App\Entity\SubDir\Category"
is deprecated since version 3.4 and won't be supported in 4.0.
Create a service named "App\Entity\SubDir\Category" instead.
Yet, this is a simple ORM bean:
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
...
How should I get rid of this issue? Do I really need to declare ORM entities as services in services.yaml? If yes, how?
Update
In fact, my entity is in a sub directory. I have amended my question.
In my service.yaml, I have tried:
App\:
resource: '../src/*'
exclude: '../src/{Entity,Repository,Tests,Entity/SubDir}'
...but to no avail.
Do you have any Classes under Service-auto registration which use an Entity as constructor argument?
That's where your problem comes from.
You need to ask yourself if the concerning class really is a service or just a plain object of which you always create the instance yourself.
If it is not used as a service through the container you have 2 options:
You can exclude this class also through the glob pattern like for example
AppBundle\:
resource: '...'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../{Entity,PathToYourNotService}'
or you can set the following parameter in your config
parameters:
container.autowiring.strict_mode: true
with this option the container won't try to create a service class with arguments that are not available as services and you will get a decisive error. This is the default setting for sf4
A good example for a class that triggers exactly this error would be a custom event class that takes an entity as payload in the constructor:
namespace AppBundle\Event;
use AppBundle\Entity\Item;
use Symfony\Component\EventDispatcher\Event;
class ItemUpdateEvent extends Event
{
const NAME = 'item.update';
protected $item;
public function __construct(Item $item)
{
$this->item = $item;
}
public function getItem()
{
return $this->item;
}
}
Now if this file isn't excluded specifically the container will try to auto register it as service. And because the Entity is excluded it can't autowire it. But in 3.4 there's this fallback which triggers this warning.
Once the strict_mode is activated the event just won't be available as service and if you tried using it as one an error would rise.
I'm actually learning how SF5 works and I have a question regarding config file injection into service or controller.
I have created an "AuthenticationBundle". Then in config/packages/authentication.yaml, I have put the configuration required for the module.
authentication:
login : 1234
password : 5678
I also created Configuration (explaining config via TreeBuilder) and Extension files inside DepedencyInjection repository of the bundle.
From there, inside load function, I am able to see the content of my configuration file.
https://symfony.com/doc/current/bundles/extension.html
But now, how can I inject it into Controller or Service ? The documentation is not enough clear on this point (at least for a new SF user point of view).
Thanks for helping.
Ok, so I reply to myself.
In the Extension file (AuthenticationExtension in my case), I set a new parameter to the service container this way :
public function load(array $configs, ContainerBuilder $container) : void
{
$configuration = new Configuration();
// merge config
$config = $this->processConfiguration($configuration, $configs);
// define the name of the key to be injected in controller or service
$container->setParameter('authentication', $config);
}
Then, in services.yaml file, I was able to pass my variable to the controller.
App\AuthenticationBundle\Controller\AuthenticationController :
arguments:
$authenticationConfig: '%authentication%' # define the name of the parameter that will be injected into the controller
After that, I just had to retrieve them in controller constructor.
public function __construct(array $authenticationConfig)
{
$this->authenticationConfig = $authenticationConfig;
}
Hope this can help somebody else.
If some Syfmony expert read my post, feel free to tell me if I'm doing things wrong.
I am working on a Symfony project, where the other developers are not really giving project-specific information, so this work is one of the worst project experience for me in the last five years. I am to create two Symfony bundles, an OrderBundle and an ErpBundle, to create some entities in the OrderBundle along with a service and a controller into ErpBundle.
The entities are normal Doctrine entities, I do not think they are important from this question's point of view. The HomeController is in the folder of
mainfolder/vendor/Myproject/myfolder/src/Myproject/App/ErpBundle/Controller/Admin/HomeController.php and it is like this:
<?php
namespace Myproject\App\ErpBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HomeController extends AdminController
{
/**
* #Route("/home", defaults={
* "_format": "html"
* })
*/
public function indexAction()
{
//some code
}
/**
* #Route( "/home/menu",
* defaults={"_format": "html"})
*/
public function indexMenuAction()
{
//some code
}
}
Its routing.yml contains this
myproject_app_erp.myproject_admin.controller:
resource: erp
type: myproject_admin
and its sevice.yml contains this
services:
# erp.example:
# class: MyprojectAppErpBundle\Example
# arguments: ["#service_id", "plain_value", "%parameter%"]
parameters:
myproject_app_erp.routing_controllers:
home: 'MyProject\App\ErpBundle\Controller\Admin\HomeController'
And, inside mainfolder/app/config/routing.yml I have this section:
myproject_app_erp:
resource: '#MyprojectAppErpBundle/Resources/config/routing.yml'
In theory this should run the HomeController I need, which, in turn would work with the service I described, which will do something with the entities, but instead of that I get the following error:
The parameter "myproject_app_erp.routing_controllers" must be defined
in erp (which is being imported from
"/var/www/mainfolder/vendor/myproject/sing/src/Myproject/App/ErpBundle/Resources/config/routing.yml").
I have tried to copy the parameters section from the service.yml into the routing.yml to have that section there, which was missed by the error message, but the error message remained. I have been trying to solve this since yesterday with a lot of approaches and I have the impression that I have more difficulties in setting up the working environment than with writing the code. I have read about service creation at the Symfony docs, this is why I have been able to reach as far as I reached, but I do not know how to solve this problem. I think I need to check how autoload works, since it seems that /home routes are controlled there, but I do not know whether I am right and how to reach that. My question is: what should I check, where and how to be able to solve this problem.
EDIT:
I have managed to avoid the error described above by implementing DependencyInjection, but creating a folder with the name of DependencyInjection into the ErpBundle and implementing the following two classes:
Configuration.php
<?php
namespace Myproject\App\ErpBundle\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('myproject_app_erp');
return $treeBuilder;
}
}
MyprojectAppErpExtension.php
<?php
namespace Myproject\App\ErpBundle\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 MyprojectAppErpExtension 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');
}
}
While the page loads now, it still uses another bundle's HomeController than the one inside ErpBundle.
It turns out that there is a custom routing-specific logic in the project and I had to do some database changes, following a project-specific convention previously unkown to me. The details are so specific, that I cannot share them here without violating the business secrets related to the project, so I know what the answer is, but cannot share this with the community.
I am developing a Symfony 3 application. Symfony profiler logs tell me:
Relying on service auto-registration for type "App\Entity\SubDir\Category"
is deprecated since version 3.4 and won't be supported in 4.0.
Create a service named "App\Entity\SubDir\Category" instead.
Yet, this is a simple ORM bean:
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
...
How should I get rid of this issue? Do I really need to declare ORM entities as services in services.yaml? If yes, how?
Update
In fact, my entity is in a sub directory. I have amended my question.
In my service.yaml, I have tried:
App\:
resource: '../src/*'
exclude: '../src/{Entity,Repository,Tests,Entity/SubDir}'
...but to no avail.
Do you have any Classes under Service-auto registration which use an Entity as constructor argument?
That's where your problem comes from.
You need to ask yourself if the concerning class really is a service or just a plain object of which you always create the instance yourself.
If it is not used as a service through the container you have 2 options:
You can exclude this class also through the glob pattern like for example
AppBundle\:
resource: '...'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../{Entity,PathToYourNotService}'
or you can set the following parameter in your config
parameters:
container.autowiring.strict_mode: true
with this option the container won't try to create a service class with arguments that are not available as services and you will get a decisive error. This is the default setting for sf4
A good example for a class that triggers exactly this error would be a custom event class that takes an entity as payload in the constructor:
namespace AppBundle\Event;
use AppBundle\Entity\Item;
use Symfony\Component\EventDispatcher\Event;
class ItemUpdateEvent extends Event
{
const NAME = 'item.update';
protected $item;
public function __construct(Item $item)
{
$this->item = $item;
}
public function getItem()
{
return $this->item;
}
}
Now if this file isn't excluded specifically the container will try to auto register it as service. And because the Entity is excluded it can't autowire it. But in 3.4 there's this fallback which triggers this warning.
Once the strict_mode is activated the event just won't be available as service and if you tried using it as one an error would rise.
I have a bundle which was working quite well for some time. However, I had to add some custom configuration params to it, so I wrote some lines in the bundle's config.yml, something like this:
# ...
acme_my_bundle:
special_params: ['param_1', 'param_2']
The configuration is defined in the bundle's Configuration class:
namespace ACME\MyBundle\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('acme_my_bundle');
$rootNode
->children()
->arrayNode('special_params')
->end()
->end();
return $treeBuilder;
}
}
The bundle is properly registered in AppKernel.php:
public function registerBundles() {
$bundles = array(
// ...
new ACME\MyBundle(),
// ...
);
// ...
return $bundles;
}
However, when I try to use my app, I get an error:
There is no extension able to load the configuration for "acme_my_bundle" (in (path_to_bundle)/MyBundle/DependencyInjection/../Resources/config/config.yml). Looked for namespace "acme_my_bundle", found none
I looked it up, but most results found were unsatisfactory - I eliminated the problems that came up during searching:
improper configuration structure
bundle not registered in the app kernel
config root node name different than the one returned from ACMEMyBundleExtension::getAlias()
I tried debugging the cause of the exception being thrown and discovered that when the YAML file loader tries to validate my config file, the container has no extensions:
var_dump($container->getExtensions()); // prints empty array - array(0) { }
It causes the validation to fail and the none part of the message to be displayed - there a no available extensions.
I tried debugging $this->extensions in ContainerBuilder::hasExtension() and for some reason the list is complete when the method is launched for the vendor bundles, but is empty for my bundle. It looks like that something in my bundle is still defined or registered incorrectly.
I changed the names off classes, etc. not to expose company code, excuse me for that if it causes confusion.
EDIT: I didn't explicitly mention it, but the Extension class is defined and the exception occurs when it is loaded - just as I have written above:
when the YAML file loader tries to validate my config file
To be more clear, here is my Extension class:
namespace ACME\MyBundle\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 ACMEMyBundleExtension 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'));
// The exception is thrown here
$loader->load('config.yml');
}
}
Check your configuration reader in ACME\MyBundle\DependencyInjection\Configuration for $rootNode = $treeBuilder->root('BUNDLE_CONFIG_KEY');.
BUNDLE_CONFIG_KEY should be:
valid (same in ACME\MyBundle\DependencyInjection\Configuration and your config.yml
unique for the app
Also please check you're defining bundle configuration in correct way - it should be added to app/config/*.yml (one of global config files). Maybe you have added acme_my_bundle config in other custom bundle config files?
You have missed bundle extension class (ACME\MyBundle\DependencyInjection\ACMEMyExtension) as explained here http://symfony.com/doc/current/cookbook/bundles/extension.html. Cookbook entry for bundle configuration is here. Key in config.yml should be named only as acme_my.
Creating the Configuration class alone is not enough. You need to register a dependency injection extension and use the Configuration class in there.
Read more about in the How to Create Friendly Configuration for a Bundle cookbook:
[The Configuration] class can now be used in your load() method to merge configurations and force validation (e.g. if an additional option was passed, an exception will be thrown)
namespace Acme\MyBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AcmeMyBundleExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// ...
}
}
Naming your extension class according to the convention will make it's automatically loaded. More on creating DIC extension classes in Creating an Extension Class. You can also enable the extension manually, see Manually registering an extension class.