Symfony : How to access configuration in controller - symfony

This may be a silly question, but i can't see how to access this data :
In the main app/config/config.yml, i have the general configuration data for my application.
parameters: #accessible by getParameter()
locale: fr
# ...
fos_user: #accessible by ???
#...
registration:
form:
from_email:
address: mymail#mydomain.fr
sender_name: TheSenderName
In my custom bundle i can access the parameters from that config.yml file with :
$this->container->getParameter('locale'); //gives me "fr" as expected
But how can i access the non parameters configuration values ?
i would like here to get the adress defined in te FOS User bundle configuration :
I can't do
$admin_adress = $this->container->getParameter('fos_user.registration.confirmation.from_email.address');
What is the right way to access thoses ?
Edit :
Yes i wanted to access this config data from FOS User Bundle (here in the example, but it could be any), inside a controller or whatever in my bundle.

I think this question nailed it
class MyProjectExtension extends Extension
{
public function load( array $configs, ContainerBuilder $container )
{
// The next 2 lines are pretty common to all Extension templates.
$configuration = new Configuration();
$processedConfig = $this->processConfiguration( $configuration, $configs );
// This is the KEY TO YOUR ANSWER
$container->setParameter( 'from_email.address', $processedConfig[ 'registration.confirmation.from_email.address' ];
// Other stuff like loading services.yml
}

You can act that way
parameters:
locale: fr
fos_user:
registration:
form:
from_email:
address: mymail#mydomain.fr
# ...
fos_user:
#...
registration:
form:
from_email:
address: %fos_user.registration.form.from_email.address%
sender_name: TheSenderName
Of course I've choose this name just to fit your controller request for parameter: you can (and maybe should) choose other keys.
However with container->getParameter you can access only parameters section of your configuration file (plus parameters exposed from vendors)

One of the solutions can be passing your controller as a service, then inject your parameters like that :
# services.yml
my.amazing.controller:
class: CompanyFirst\LabBundle\Controller\PloufController
arguments:
- '#filesystem'
- '%dump_file_convention%' // your config parameter

Related

Kayue\WordpressBundle with Symfony 4.4.1: The class XXX was not found in the chain configured namespaces App\Entity

I'm trying to work with kayue/KayueWordpressBundle installed with composer as composer require kayue/kayue-wordpress-bundle in my Symfony 4.4.1 project but I'm unable to.
This is what I'm trying to do:
<?php
namespace App\Service\WordPress;
use Doctrine\ORM\EntityManagerInterface;
use Kayue\WordpressBundle\Entity\Post;
class PostCollection
{
protected $postRepository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->postRepository = $entityManager->getRepository(Post::class);
}
}
The error I get:
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
At first I blamed my dual-database configuration (Symfony is on a different DB from Wordpress) but then I put the DBs together and the issue persists:
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# Only needed for MySQL (ignored otherwise)
charset: utf8mb4
default_table_options:
collate: utf8mb4_unicode_ci
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
I've been fiddling for the past 2hrs, but now I'm fresh out of ideas. I wonder if ANYONE actually got this to work with Symfony 4.
Thanks!
Edit: other tries:
Direct post injection:
use Kayue\WordpressBundle\Entity\Post;
public function index(Post $post){}
Result:
Cannot autowire argument $post of "App\Controller\IndexController::index()": it references class "Kayue\WordpressBundle\Entity\Post" but no such service exists.
As per documentation: outdated Symfony 2 way
$repo = $this->get('kayue_wordpress')->getManager()->getRepository('KayueWordpressBundle:Post');
Result:
Service "kayue_wordpress" not found: even though it exists in the app's container, the container inside "App\Controller\IndexController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Try using dependency injection instead.
The "best way" to do this would actually be:
public function index(EntityManagerInterface $entityManager)
{
$entityManager->getRepository('KayueWordpressBundle:Post');
}
Result:
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
Although you found a solution to this, there is a chain of issues I would like to explain.
The error
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
means that in the entity manager provided, whose config is defined at:
orm:
...
mappings:
App:
...
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
the entity type Kayue\WordpressBundle\Entity\Post was not found.
Usually this type of error is solved by:
include the path Kayue\WordpressBundle\Entity in the entity manager or
use another entity manager which includes this path
In your case the default entity manager is autowired, based on the service alias of Doctrine\ORM\EntityManagerInterface as explained here. The alias is defined in the doctrine bundle `s config which points to the default doctrine entity manager.
You want to use Kayue\WordpressBundle `s entity manager, not the default one.
Solution
To solve this you can
1) Bind Arguments By type, as you find out, creating an alias of Kayue\WordpressBundle\Wordpress\ManagerRegistry to the service kayue_wordpress, as:
services:
# pass this service for any ManagerRegistry type-hint for any
# service that's defined in this file
Kayue\WordpressBundle\Wordpress\ManagerRegistry: '#kayue_wordpress'
or
2) use Binding Arguments by Name, in this case the "$wpManagerRegistry", as:
services:
# default configuration for services in *this* file
_defaults:
...
bind:
$wpManagerRegistry: '#kayue_wordpress'
and then
public function index($wpManagerRegistry)
{
$postRepository = $wpManagerRegistry->getManager()->getRepository('KayueWordpressBundle:Post');
so that any argument with name "$wpManagerRegistry" is autowired to this service.
References
The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc)
My collegue found a solution. You must configure the autowire like this:
// config/packages/kayue_wordpress.yaml
services:
Kayue\WordpressBundle\Wordpress\ManagerRegistry: '#kayue_wordpress'
After that, you can autowire:
use Kayue\WordpressBundle\Wordpress\ManagerRegistry;
public function __construct(ManagerRegistry $wpManagerRegistry)
{
$this->wpPostRepository = $wpManagerRegistry->getManager()->getRepository('KayueWordpressBundle:Post');
}
public function getPosts()
{
$post = $this->wpPostRepository->findOneBy(array(
'slug' => 'hello-world',
'type' => 'post',
'status' => 'publish',
));
}

Symfony CMF Dynamic Router no contentDocument

Following the Dynamic Router doc, I can:
Create a route linked to my content (Page entity)
Persist it with ORM
Load my controller matching the route
But as my controller should expect the $contentDocument parameter, I have nothing.
Here's how I create my route and my entity:
$page = new Page();
$page->setTitle('My Content')
->setBackground(1)
->setDescription('My description')
->setContent('<h1>Hello</h1>');
$manager->persist($page);
$manager->flush(); // flush to be able to use the generated id
$contentRepository = $this->container->get('cmf_routing.content_repository');
$route = new Route();
$route->setName('my-content');
$route->setStaticPrefix('/my-content');
$route->setDefault(RouteObjectInterface::CONTENT_ID, $contentRepository->getContentId($page));
$route->setContent($page);
$page->addRoute($route); // Create the backlink from content to route
$manager->persist($page);
$manager->flush();
Here's my config section:
cmf_routing:
chain:
routers_by_id:
router.default: 200
cmf_routing.dynamic_router: 100
dynamic:
enabled: true
persistence:
orm:
enabled: true
generic_controller: AppBundle:Default:showPage
templates_by_class:
AppBundle\Entity\Page: AppBundle:Default:index.html.twig
My controller:
public function showPageAction($page)
{
return $this->render('default/index.html.twig');
}
And the error:
Controller "AppBundle\Controller\DefaultController::showPageAction()" requires that you provide a value for the "$page" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
Dynamic routing puts the content document into the request with the name "contentDocument". You need to explicitly use that name:
public function showPageAction($contentDocument)
{
return $this->render('default/index.html.twig');
}
if you do not need to do anything in your controller, you don't need to specify the generic_controller. template_by_class will make the bundle provided controller render that template with the page instance found in $contentDocument.

SymfonyBundle: How to pass to a controller as a service the full bundle configuration

I've created a bundle completely separate from the main app, so I install it via Composer.
This bundle requires some sort of configuration:
# app/config/config.yml
shq_mybundle:
node1:
node1_1:
...
node1_2:
...
node2:
node2_1:
...
node2_2:
...
My bundle also has a controller MyBundleAController and this controller has a ––construct() with this signature:
class MyBundleAController
{
public function __construct(EntityManagerInterface $entityManager, EventDispatcherInterface $eventDispatcher, array $config)
{
$this->entityManager = $entityManager;
$this->eventDispatcher = $eventDispatcher;
$this->config = $config;
}
}
My bundle also loads a services.yml file that uses autowiring to configure controllers:
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to d
SerendipityHQ\Bundle\MyBundle\Controller\:
resource: '../../Controller/*'
public: false
tags: ['controller.service_arguments']
Obviously, the way MyBundleAController is configured throws an error, as the $config argument is unknown by the autowiring functionality that requires the argument be typehinted or explicitly set:
Cannot autowire service
"SerendipityHQ\Bundle\MyBundle\Controller\MyBundleAController":
argument "$config" of method "__construct()" must have a type-hint or
be given a value explicitly.
And here we arrive to my question: the $config parameter is the one that someone sets in its app/config/config.yml, so this one:
# app/config/config.yml
shq_mybundle:
node1:
node1_1:
...
node1_2:
...
node2:
node2_1:
...
node2_2:
...
How can I pass shq_mymodule configuration to the autowired controller?
In a first attempt, I've tried to do something like this
SerendipityHQ\Bundle\MyBundle\Controller\ConnectController:
arguments:
$config: "%shq_mybundle%"
But obviously this doesn't work.
To make it work I should do something like this in MyBundleExtension:
$container->setParameter('shq_mybundle', $config);
This way I transform it in a parameter accessible from the services.yml file that can use it to autowire the MyBundleAController controller.
But this seems to me like an hack: is there a more elegant way to do this?

Symfony2 multi-level dynamic router

I have a current project that has to displays both defined pages with specific entities, what is very easy to manage with Symfony2, and content pages on different layouts, what is - I guess - a bit less common.
I get in trouble trying to build the routing system.
For instance, if I have to display a page with some news,
I would like to update the router of my bundle with a new route like :
my_bundle_news_page:
pattern: /news
defaults:
_controller: MyBundle:NewsController:indexAction
But how to manage a dynamic router that could have a totally custom URL on many levels ?
Let's imagine I've got a "Page" Entity, that is self-references for an optionnal "parent-child" relation.
I don't think I can just use any config YAML file for this specific routing ?!
my_bundle_custom_page:
pattern: /{slug}
defaults:
_controller: MyBundle:PageController:showAction
This would bind all the first-level pages:
/projects
/about
/contact
/our-project
What about a page that would be displayed with, for instance, a slug like:
/our-project/health
In fact any URL...
/{slug-level1}/{slug-level2}/{slug-level3} etc.
Cause the pages are supposed to change and be updated from webmastering.
I guess the best way would be to have a router that compare the {slug} with a database field (entity property)
I read in the Symfony-CMF doc that it is possible to write a service based a route provider:
namespace MyBundle\Routing;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route as SymfonyRoute;
use MyBundle\Entity\PageRepository;
class RouteProvider extends PageRepository {
public function findPageBySlug($slug)
{
// Find a page by slug property
$page = $this->findOneBySlug($slug);
if (!$page) {
// Maybe any custom Exception
throw $this->createNotFoundException('The page you are looking for does not exists.');
}
$pattern = $page->getUrl(); // e.g. "/first-level/second-level/third-level"
$collection = new RouteCollection();
// create a new Route and set our page as a default
// (so that we can retrieve it from the request)
$route = new SymfonyRoute($pattern, array(
'page' => $page,
));
// add the route to the RouteCollection using a unique ID as the key.
$collection->add('page_'.uniqid(), $route);
return $collection;
}
}
But how to set it up as a service ? Are there some requirements ?
How could this kind of thing work, does it add a route to the RouteCollection when request is called ?
And will I be able to bind any route in this way ?
EDIT : services.yml of my bundle
parameters:
cmf_routing.matcher.dummy_collection.class: Symfony\Component\Routing\RouteCollection
cmf_routing.matcher.dummy_context.class: Symfony\Component\Routing\RequestContext
cmf_routing.generator.class: Symfony\Cmf\Bundle\RoutingBundle\Routing\ContentAwareGenerator
cmf_routing.nested_matcher.class: Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
cmf_routing.url_matcher.class: Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
fsbcms.chain_router.class: Symfony\Cmf\Component\Routing\ChainRouter
fsbcms.route_provider.class: FSB\CMSBundle\Routing\RouteProvider
fsbcms.dynamic_router.class: Symfony\Cmf\Component\Routing\DynamicRouter
fsbcms.route_entity.class: null
services:
fsbcms.router:
class: %fsbcms.chain_router.class%
arguments:
- "#logger"
calls:
- [setContext, ["router.request_context"]]
fsbcms.route_provider:
class: "%fsbcms.route_provider.class%"
arguments:
- "#doctrine"
cmf_routing.matcher.dummy_collection:
class: "%cmf_routing.matcher.dummy_collection.class%"
public: "false"
cmf_routing.matcher.dummy_context:
class: "%cmf_routing.matcher.dummy_context.class%"
public: false
cmf_routing.generator:
class: "%cmf_routing.generator.class%"
arguments:
- "#fsbcms.route_provider"
- "#logger"
calls:
- [setContainer, ["service_container"]]
- [setContentRepository, ["cmf_routing.content_repository"]]
cmf_routing.url_matcher:
class: "%cmf_routing.url_matcher.class%"
arguments: ["#cmf_routing.matcher.dummy_collection", "#cmf_routing.matcher.dummy_context"]
cmf_routing.nested_matcher:
class: "%cmf_routing.nested_matcher.class%"
arguments: ["#fsbcms.route_provider"]
calls:
- [setFinalMatcher, ["cmf_routing.url_matcher"]]
fsbcms.dynamic_router:
class: "%fsbcms.dynamic_router.class%"
arguments:
- "#router.request_context"
- "#cmf_routing.nested_matcher"
- "#cmf_routing.generator"
tags:
- { name: router, priority: 300 }
I suggest taking a look at the Symfony CMF routing component and the CmfRoutingBundle (to implement the component in symfony).
The Routing component uses a chain router, which is irrelevant for this question but it's good to know. The chain router chains over a queue of routers. The component provides a DynamicRouter that uses a NestedMatcher. That's exactly what you want.
The NestedMatcher uses a Route provider to get the routes from a dynamic source (e.g. a database). You are showing an example of a Route provider in your question.
Furthermore, it uses a FinalMatcher to match the route. You can just pass an instance of Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher, as you are doing not too difficult things.
Take a look at the docs of the RoutingBundle to learn how to activate the chain router and then create a route provider which loads the routes, make a service:
acme_routing.route_provider:
class: Acme\RoutingBundle\Provider\DoctrineOrmProvider
arguments: ["#doctrine"]
Now, you can create a NestedMatcher service:
acme_routing.url_matcher:
class: Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher
arguments: ["#cmf_routing.matcher.dummy_collection", "#cmf_routing.matcher.dummy_context"]
acme_routing.nested_matcher:
class: Symfony\Cmf\Component\Routing\NestedMatcher
arguments: ["#acme_routing.route_provider"]
calls:
- [setFinalMatcher, ["acme_routing.url_matcher"]]
Now, register the DynamicRouter and put it in the chain:
acme_routing.dynamic_router:
class: Symfony\Cmf\Component\Routing\DynamicRouter
arguments:
- "#router.request_context"
- "#acme_routing.nested_matcher"
- "#cmf_routing.generator"
tags:
- { name: router, priority: 300 }
Now, it should work and should load the routes from the database and match them against the request.

FOSUserBundle : handling different types of user

I'm starting a new project, using a symfony2 architercture and the usefull FosUserBundle to handle my users.
In my application, i have 2 different types of users, which are : User and Entreprise.
I created a Userform (with embedded form in it).
Now i want to create a EntrepriseForm (with a different embedded form) but i'm stuck, due to FosUserConfiguration allowing only 1 registration form type.
If it can helps, here is how my config.yml looks like :
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: Digin\UserBundle\Entity\User
registration:
confirmation:
from_email: # Use this node only if you don't want the global email address for the confirmation email
address: xxx#xxx.org
sender_name: xxx
enabled: true # change to true for required email confirmation
template: FOSUserBundle:Registration:email.txt.twig
form:
type: mybundle_user_registration
handler: fos_user.registration.form.handler.default
name: fos_user_registration_form
validation_groups: [Registration]
Any idea about how i should do that ?
On your place I will add a new Role in your security.yml(remember to start it with ROLE_ symfony ignores other roles). Then add the Listener InteractiveLoginListener to do some extra stuff redirections after login on diffrent roles. And then add JMSSecurityExtraBundle That will help you to validate the access in accions and controllers just by annotations:
use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
class MyService
{
/** #PreAuthorize("hasRole('A') or (hasRole('B') and hasRole('C'))") */
public function secureMethod()
{
// ...
}
}
Look at the documentation: http://jmsyst.com/bundles/JMSSecurityExtraBundle/master/installation

Resources