Iam trying to implement service for encoding password but it seems it doesn't work cause I get "You have requested a non-existent service user_service " error.
Here is my code :
Vendor/BundleNameBundle/Resources/config/services.yml
services:
user_service:
class: Morescreens\VideomanagerBundle\Service\UserService
arguments: ['#security.encoder_factory']
app/config/config.yml
imports:
- { resource: "#morescreensVideomanagerBundle/Resources/config/services.yml" }
code for my service
class UserService {
/**
* #var EncoderFactoryInterface
*/
private $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory=$encoderFactory;
}
public function setEncodedPassword(User $user)
{
$encoder=$this->encoderFactory->getEncoder($user);
$password=$encoder->encodePassword($user->getPassword(),$user->getSalt());
$user->setPassword($password);
}
}
Inside mine controller:
$user=new User();
$user->setPassword('password');
$this->get('user_service')->setEncodedPassword($user);
EDIT:
I manually deleted cache folder and my service started to work.
Try looking at your src/{VendorName}/{BundleName}Bundle/DependencyInjection/{VendorName}{BundleName}Extension.php
This file should load your service definitions like this:
<?php
namespace Vendor\{BundleName}Bundle\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 Vendor{BundleName}Extension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
//Load our YAML resources
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
This will load the service definition from src/{VendorName}/{BundleName}Bundle/Resources/config/services.yml into the container. If that still doesn't work try doing php app/console cache:clear. For speed reasons, symfony will basically aggregate all your service (and other configuration files) into a cached file so it doesn't have to read those files every time. The files that actually cache the service definitions are located:
app/cache/{envName}/ (The cache directory can be configured to be located wherever you want, this is just the default).
The files that have the relevent info for container service definitions are:
app{EnvName}DebugProjectContainer.php
app{EnvName}DebugProjectContainer.php.meta
app{EnvName}DebugProjectContainer.xml
Related
I get following error:
There is no extension able to load the configuration for
"upload_images" (in
"/var/www/vhosts/diabetigraph-dev/vendor/verzeilberg/upload-images/src/Resources/services.yaml").
Looked for namespace "upload_images", found "none"
This are my files:
services.yaml
services:
verzeilberg\UploadImagesBundle\Service\Rotate:
autowire: true
upload_images:
version: 100
Configuration
namespace verzeilberg\UploadImagesBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('upload_images');
$treeBuilder->getRootNode()
->children()
->integerNode('version')->end()
->end();
return $treeBuilder;
}
}
UploadImagesExtension
namespace verzeilberg\UploadImagesBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class UploadImagesExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources'));
$loader->load('services.yaml');
$config = $this->processConfiguration(new Configuration(), $configs);
$container->setParameter('version', $config['version']);
}
}
What am I doing wrong?
The basic answer is that your upload_images configuration needs to be moved to it's own application level config/packages file:
# app/config/packages/upload_images.yaml
upload_images:
version: 100
In your bundle, the Configuration object represents the bundle's configuration. There is no upload_images.yaml sort of file in your bundle. The object is quite powerful so you can add defaults and options and what not.
Your bundle's extension takes care of processing the final configuration and making information such as parameters available to the rest of the system:
class UploadImagesExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// $configs is actually an array of array representing the contents of upload_files.yaml
$config = $this->processConfiguration(new Configuration(), $configs);
$container->setParameter('upload_files.version', $config['version']);
// Loading your bundle's services.yaml file is a different process which just happens to be kicked off by the loader
// Thanks to the setParameter above, %upload_files.version% can be used by services
$loader = new YamlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources'));
$loader->load('services.yaml');
}
}
It can be confusing and for me at least I had to read the docs multiple times and do quite a bit of experimenting to understand the overall process.
The confusion is one of the reasons that Symfony evolved from multiple bundles per application to one application bundle to no application bundle at all.
I might also add the Symfony uses composer recipes to simplify installing a bundle. The recipe not only adds the bundle to config/bundles.php but copies any default configuration file to config/packages as well. Unfortunately, there are additional steps required for this copy to happen (see the docs) so it's easiest to do things the old-fashion way and just tell the developer to create the config file via the bundle's README file.
I'm creating a PHP package that will reside in the /vendor directory and it needs to access the database using Doctrine. In my main Symfony 4.2 code, I can access the database by using autowiring in the constructors like this:
public function __construct(EntityManagerInterface $em)
However, this only works with Symfony and does not seem to work inside public bundles. Please note that I need to access the default database connection, I do not want to create a new connection if one already exists.
I am trying to create a package that will work both with and without Symfony, so what I really need is the raw PHP to get the current database connection (not a new connection) without all the autowiring magic.
If the package is running under Symfony, it should use the current Symfony database connection. If not, it should create its own connection.
For example, I can open a new connection like this:
$conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config)
That works fine, but I need a way to get the current Symfony database connection (something like this):
$conn = \Doctrine\DBAL\DriverManager::getCurrentConnection()
Unfortunately, there is no such function and I can't figure out how to create my own version of it.
You should define the class as a service and inject the EntityManager as an argument:
# vendor/Acme/HelloBundle/Resources/Config/services.yml
services:
acme_hellp.service_id:
class: Acme\HelloBundle\Class
arguments: ['#doctrine.orm.entity_manager']
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
class AcmeHelloExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
);
$loader->load('services.xml');
}
}
Also, check this documentation on the Symfony site how to load your config files:
https://symfony.com/doc/current/bundles/extension.html
Let me understand your problem, are you looking for a package to share using the doctrine Entity Manager of Symfony? so let me share with you my experience.
That is possible, and there are no solution visible to read or watch, so you need to review some packages to understand how they resolve the problem, the package i review was on this link, this package provide a support not only doctrine, also others platforms like Mongo, so let me share with you how they made the solution.
First, you need to crete a package in a bitbucket or github, and made this package as "type": "symfony-bundle". i read the comments before and you are correct, you need to create a Configuration and Extension file to enable you packages, so in your extension file you need to implement a Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface this package will allow you make the magic. so you can write some lines of code similar of this and replace for your namespace:
<?php
namespace NameSpaceBundle\CoreBundle\DependencyInjection;
use Exception;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
/**
* Class NameSpaceBundleExtension Configurations of the Core
* #package NameSpaceBundle\CoreBundle\DependencyInjection
*/
class NameSpaceBundleExtension extends Extension implements PrependExtensionInterface
{
/**
* {#inheritdoc}
* #param array $configs
* #param ContainerBuilder $container
* #throws Exception
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('routing.yml');
$this->processConfiguration(new Configuration(), $configs);
}
/**
* {#inheritdoc}
* #param ContainerBuilder $container
*/
public function prepend(ContainerBuilder $container)
{
$configs = $container->getExtensionConfig($this->getAlias());
$this->processConfiguration(new Configuration(), $configs);
$doctrineConfig = [];
$doctrineConfig['orm']['mappings'][] = array(
'name' => 'NameSpaceBundle',
'is_bundle' => true,
'type' => 'annotation',
'prefix' => 'NameSpaceBundle\CoreBundle\Entity'
);
$container->prependExtensionConfig('doctrine', $doctrineConfig);
}
/**
* {#inheritdoc}
* #return string
*/
public function getAlias()
{
return 'bundelName_core';
}
}
Review your service.yaml allocated on Resouces/config and now you can inject not only EntityManagerInterface.
services:
_defaults:
autowire: false # Automatically injects dependencies in your services.
autoconfigure: false # Automatically registers your services as commands, event subscribers, etc.
public: false
NameSpaceBundle\CoreBundle\Manager\Company:
public: false
class: NameSpaceBundle\CoreBundle\Doctrine\CompanyManager
arguments:
- '#doctrine'
- '\NameSpaceBundle\CoreBundle\Entity\Company'
Also this aprouch is similar of some packages of FriendsOfSymfony and the package i tell you before.
PD. Sorry for my english is not my native language
After a couple days of trying to create a Symfony bundle to make things work the "official" way, I figured out a one-liner hack that gets the Symfony Doctrine connection and does what I need. It relies on the fact that $kernel is a Symfony global:
// Singleton to get Doctrine database connection
//
// If we are running on the legacy system, it will open a new database connection.
//
// If we are running under Symfony, it will use the existing Doctrine
// connection.
class Doctrine
{
static private $instance = null;
private $conn;
// The database connection is established in a private constructor
private function __construct()
{
global $kernel;
if (class_exists('\config')) {
// running on legacy system
$cfg = new \config();
//
// set up Doctrine connection
//
$params = [
'driver' => 'pdo_mysql',
'user' => $cfg->db_username,
'password' => $cfg->db_password,
'host' => $cfg->db_hostname,
'dbname' => $cfg->db_name,
];
$config = new \Doctrine\DBAL\Configuration();
$this->conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config);
} else {
// running on Symfony system
$this->conn = $kernel->getContainer()->get('doctrine.orm.default_entity_manager')->getConnection();
}
}
static public function getInstance()
{
if (!self::$instance) {
self::$instance = new Doctrine();
}
return self::$instance;
}
static public function connection()
{
return self::getInstance()->getConnection();
}
public function getConnection()
{
return $this->conn;
}
}
By using the above class, this simple statement will get a Doctrine connection in both Symfony and our legacy code:
$conn = Doctrine::connection()
Now we can the same libraries on both Symfony and our legacy code. Yes, I know it's a hack, but it is easier to understand and maintain than the 10+ files and dozens of lines of code required to create even a simple Symfony bundle (and the legacy system won't recognize a bundle). I've put this class in a private composer package that can be used by both legacy systems and Symfony. This will make it much easier to keep things running until we can migrate all the legacy stuff to Symfony. After that is done, then I can do things the right way.
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'm using Symfony 4 and there is a configuration file which needs to be injected into the command App\Command\FooCommand in the form of an array. I have a custom DI extension registered at App\Kernel::configureContainer(), which's used to validate the custom configuration file (for the convenience of the development, the config is big and going to be changed frequently during the dev). The constructor of the command is public function __construct(Foo $foo, array $config), I'm expecting the config as the second argument.
Now how do I put this config there? The documentation says about parameters, but it's not a parameter. I was thinking of changing the definition of the command and adding this argument in the Extension::load method like this:
class FooExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
//inject the configuration into the command
$fooCmdDef = $container->getDefinition(FooCommand::class);
$fooCmdDef->addArgument($config);
}
}
but it ends up with the error
You have requested a non-existent service "App\Command\FooCommand".
However the command must have been registered as a service automatically.
What am I doing wrong and how to inject this config?
You cannot access any service in a DI extension Class because the container was not yet compiled. For your case is common to create a Compiler Pass where you will be able to retrieve the needed service and apply any modification to it.
For example you can create a parameter in the container extension on where store the configuration:
class FooExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
//create a container parameter
$container->setParameter('your_customized_parameter_name', $config);
}
}
Then retrieve what you need in the compiler pass and then apply some modification:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class YourCompilerPass implements CompilerPassInterface
{
/**
* {#inheritdoc}
*/
public function process(ContainerBuilder $container)
{
# retrieve the parameter
$config = $container->getParameter('your_customized_parameter_name');
# retrieve the service
$fooCmdDef = $container->getDefinition(FooCommand::class);
# inject the configuration
$fooCmdDef->addArgument($config);
# or you can also replace an argument
$fooCmdDef->replaceArgument('$argument', $config);
}
}
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.