Symfony2. Howto multiple config files in a bundle - symfony

I just can't figure this out.
I am writing a report builder in Symfony2.
I have a config file like this:
bundle:
sections:
Report1:
buckets:
bucket1:
...
calculations:
calculation1:
...
report:
rows:
row1:
...
For several reports, this gets to be loooong.
I've tried breaking this file into smaller files and loading them separately. This didn't work. Here's what I tried:
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('bundle_report.yml');
$loader->load('bundle_report_1.yml'); // Order is important here
$loader->load('bundle_report_2.yml');
$loader->load('bundle_report_3.yml');
$loader->load('services.yml');
}
What's the best way to do this? Is it even possible?
The error I'm getting is (exception is thrown before $loader->load()s happen):
The child node "sections" at path "bundle" must be configured
If I switch the order ( $loader->load()s first, then new Configuration()):
There is no extension able to load the configuration for "bundle"
Configuration looks like this:
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('bundle');
$rootNode
->children()
->arrayNode('sections')->isRequired()
->prototype('array')
...

Here's what I did.
Loaded it all into one 'bundle.yml' config file.
Put that file in app/config
Imported it in app/config.yml imports section
Defined the 'Configuration' class in my bundle (see above). Bundle generator created this.
Then, in my BundleReportExtension class, added these lines
// Parse our configuration. It's included in /app/config/config.yml
// Parser is Configuration class in this package.
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// Great. Parsed. Now copy to parameters for later use.
$container->setParameter('bundle.default_config', $config['default_config']);
$container->setParameter('bundle.sections', $config['sections']);
Now, I can't get the configuration as an array with:
$container->setParameter('bundle.sections');
In much searching this is the best I could come up with. If you have other suggestions, please share.

You are not supposed to load your config files from the load method.
This is made to
load the base config from Configuration class
compare it to custom config (yml files in app/config/)
and load the bundle services
All your config files must be in app/config/ to be passed as parameters to the load method. As you do it here, they are useless.
That's why you get
The child node "sections" at path "bundle" must be configured
And if you reports are evolving, maybe it's better to put this in a business logic (model/entities) and to plug an admin (eg: sonata admin).

Related

Symfony Bundle that needs a file that differs per project

I've written a small newsletter bundle for the multiple sites I host/develop for. The newsletter fetches the recipients from a newsletter source that differs from project to project. Could be a csv file, could be a database, ...
So in my Controller I thought about writing a NewsletterQueueImportModel() which is being called upon hitting the button "import".
...
$import = new NewsletterQueueImportModel();
$subscribers = $import->getSubscribers($this->getDoctrine());
...
However this file is still delivered with my bundle and in the vendor folder. So I need to change this file on a per project basis.
Override the file, but how? I do not think this is possible.
Remove the file from the newsletter bundle itself and refer to AppBundle/NewsletterQueueImportModel (e.g. use AppBundle instead of use NewsletterBundle - downsides: all projects need to be named AppBundle and I feel it is poor design
I thought about registering a service or something like that, but I really don't know the beste way to do. So what is the best way to create a file on which a bundle depends but differs from project to project?
Well, I have been doing something similar but with views.
So I had 2 sites running on one bundle - in one site I needed to see some sections which i don't need in other site.
I managed to do that with configuration.
1) In each of your site - app/config/config.yml you can define parameters. In my case it was something like
reviews_admin:
views:
table_favorite_block: true
table_brand_view: true
table_image_view: true
2) Then in bundle you must create folder named DependencyInjection with 2 files in it. Configuration and your bundle extension
class Configuration implements ConfigurationInterface
{
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('reviews_admin', 'array');
$rootNode
->children()
->arrayNode('views')
->children()
->booleanNode('table_favorite_block')->defaultTrue()->end()
->booleanNode('table_brand_view')->defaultTrue()->end()
->booleanNode('table_image_view')->defaultTrue()->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
Extension
class ReviewsAdminExtension 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->setParameter('reviews_admin_view', $config['views']);
}
}
I'm not sure if this will suite your situation, but for me it seems like the most convenient way how to manage things in bundles which depends on projects.
Also you can try to make one base class in bundle (which contains things that will be same for all projects (for imports))
And then extend it in site side ?

Symfony bundle config parameters not available in listener?

I have a bundle that has a listener which I have configured:
class Configuration implements ConfigurationInterface
{
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder ()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('mybundle_name');
$rootNode
->children()
->scalarNode('name')->defaultValue('value')
->end()
;
return $treeBuilder;
}
}
I also have a listener which has a few services injected into, primarily doctrine and the container parameters:
services:
app.router_subscriber:
class: MyBundle\EventSubscriber\RequestSubscriber
calls:
- [setEntityManager, ['#doctrine.orm.entity_manager']]
- [setContainer, ['#service_container']]
tags:
- { name: kernel.event_subscriber }
When I dump the $this->container I can see the parameters except my own defined above.
When I run
bin/console config:dump-reference MyBundle
I do see what I am expecting
What am I missing to have my bundle parameters get merged into the application parameters? I am seeing third party bundles listed but not my own bundle. I followed the docs as verbatim as I could so the conventions have been followed as far as I am aware...
EDIT | I haven't created a bundle config.yml file - I assumed the Configuraiton object did that for me - setting the schema and default values - which could be overridden by application configs (if desired). Do I need to specifcy a bundle config.yml and import into application something like this (Merge config files in symfony2)?
Ideas?
I wrote a couple blog posts showing how you can set bundle configuration defaults using YAML files, and a follow-up on how to automatically set bundle configuration values as container parameters. This was for Symfony2 and written in 2014, and the particular section of the Symfony documentation I link to disappeared from Symfony 2.3 onward, but the same concept still applies.
The main takeaway from those posts is that you can set your configuration values as container parameters in your bundle's Extension class via the load() method manually like so:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter($this->getAlias().'.name', $config['name']);
}
Notice that you can call $this->getAlias() to get the bundle's root name (mybundle_name). Using the above call you would then have a parameter defined as mybundle_name.name which you could then override in your application's config.yml if need be.

Symfony not respecting overriding class argument for test environment

I have a bundle with a services.yml where the service definition uses a parameter contained within the same file for the class parameter, like so:
parameters:
application.servicename.class: Application\Service\ServiceName
services:
application.servicename:
class: %application.servicename.class%
Now I want to override the service class for my test environment. However, overriding the parameter in config_test.yml does not result in an object of the overriding class being instantiated.
Adding the following to config_test.yml:
parameters:
application.servicename.class: Application\Mock\Service\ServiceName
...still causes the service to be instantieted from Application\Service\ServiceName. If I try passing application.servicename.class as an argument to the service and dump it in the constructor, the overriden value of Application\Mock\Service\ServiceName is displayed.
Why is Symfony not respecting the overridden value for the service class when preparing the service?
You should move
parameters:
application.servicename.class: Application\Service\ServiceName
From services.yml to config.yml becasuse in my opninion you are overriding the value of the paremeter in config_test.yml with the value you have in services.yml
I think what you're looking for is a Extension class in your Bundle:
http://symfony.com/doc/current/cookbook/bundles/extension.html
I think you might be able to change priorities loading the config files
Here's an example of implementation
public function load(array $configs, ContainerBuilder $container {
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('your.config.parameter', $config['your']['config']['parameter']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
}
As it turns out, this problem was not related to Symfony loading configuration but rather an assumption that the incorrect class was loaded. This assumption was caused by the fact that the method calls of both the original service (and the mock extending it) was marked as private.
Had this not been a problem, I belive what I was attempting to do should be possible, ref http://symfony.com/doc/2.8/cookbook/bundles/override.html#services-configuration
Sorry to waste your time.

Symfony2 How to get the Web or MyBundle directory from inside a controller?

Symfony2. In a controller, I would like to define a path to the Resources/public/pdf directory of a bundle of mine.
If I use __DIR__, I get the path to the Controller directory of my bundle; if $this->get('kernel')->getRootDir(), the path to the App directory. Either way I am not able to move back to the upper directory.
How do I do?
ps. something like $this->get('kernel')->getRootDir().'/../src/ renders .../app/../src/.
EDIT I got what i need by a very simple minded and ugly fix:
$path001 = $this->get('kernel')->getRootDir().'/';
$path002 = explode('/', $path001);
$path003 = array_pop($path002);
$path003bis = array_pop($path002);
$path004 = implode("/", $path002);
$path_pdf = $path004.'/src/Mario/<MyBundle>/Bundle/Resources/public/pdf';
Is there a better way?
The realpath function (http://php.net/manual/en/function.realpath.php) can clean up all the dot dot stuff if it bothers you.
// From a controller
$resourceDir = realpath(__DIR__ . '/../Resources');
Of course this only works if the controller is in a fixed directory and never moves.
I like to set a parameter using the dependency injection extension.
class CeradProjectExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter(
'sportacus_project_resources_dir',
realpath(__DIR__ . '/../Resources')
);
The path can be injected or retrieved from the container.
yes, there is:
$kernel = $this->container->getService('kernel');
$path = $kernel->locateResource('#MarioMyBundle/Resources/public/pdf/file.pdf');
as per Accessing Files Relative to Bundle in Symfony2

Symfony2 cache:clear outputs the content of services.yml file

In Symfony2, everytime I clear my cache via the console:
php app/console cache:clear
The console prints out the contents of my services.yml file! If I manually delete the cache via rm -rf app/cache/* (which I have to do since my console user doesn't have access to the apache user www-data, for some reason, despite being in the same group because the files are created as 640 instead of 660), then the public website also prints it out the FIRST time the page is loaded and the cache is generated.
NOTE: this prints out even if services.yml is NOT loaded in the app/config/config.yml (just by existing, somehow it's being referenced)
We import the services.yml file:
# /app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: "#AcmeBundle/Resources/config/services.yml" }
Then set global services in the services.yml file:
# /src/Acme/Bundle/Resources/config/services.yml
# TODO: ALERT! this prints out whenever I clear the cache...
services:
#This is a service so that we can access the view object anywhere
acme.view:
class: Acme\Bundle\Controller\ViewController
arguments: [ #doctrine.orm.entity_manager ]
Question: Any ideas why this file is printing out every time I clear the cache?
This was caused by changing the /Acme/Bundle/Resources/config/services.yml services parameters from PHP to YAML format (I created as PHP originally in my testing).
The reference to the service parameters file is hard coded in the /Acme/Bundle/DependencyInjection/AcmeBundleExtension.php in two places.
Here is the broken code:
class AcmeBundleExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
Since the services.yml was being loaded as a PHP file, it was just printing out text whenever the cache was recreated. Amazingly, all the services still actually loaded somehow...!
So take note, if you change the config file from PHP to YAML (or vice versa) you have to update:
$loader->load('services.yml');
(which I did)
But also you must update the loader function from Loader\PhpFileLoader to Loader\YamlFileLoader:
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
Of course that seems obvious, but if you are new to Symfony, take note that converting formats of your service config file requires more than just changing your file name.

Resources