Conflicts in routing in symfony between bundles - symfony

Playing around with symfony. I have two bundles and each bundle has a controller within it. Just to see how routing works I gave the same path to functions within both controllers. Bundle B was the newly created bundle and when the URL app/simple was hit I got a response from bundle B always. Just curious as to whether there is any logic behind this.
PS: I know this is bad practice but just wanted to see how the guts of routing in works.
/**
* #Route("/app/simple", name="homepage")
*/
public function indexAction()
{
return new Response('Hello From bundle A!');
}
In bundle B
/**
* #Route("/app/simple", name="homepage")
*/
public function indexAction()
{
return new Response('Hello From bundle B!');
}

Your app has a single routing configuration which can include other configurations. Probably app/config/routing.yml.
That configuration file will include the routes for your bundles by using the resource key that can import routes from another routing.yml file or from annotations in a PHP controller.
The order of those will determine which route gets chosen since Symfony2 always uses the first matching route.

Related

Proper Symfony Package to Consume and Provide API via REST

I need to create a middleware app that provides an api and consumes two apis. However, I am having a problem with two Symfony packages: HTTP Client and HttpFoundation
I don't understand when I should be using one versus the other. Little new to Symfony and I've read the docs but they both allow for you to consume and provide and api vis request and response methods or classes depending on which package you use.
I know I'm missing something obvious but I'm having a hard time of it. Any help would be greatly appreciated.
You should have to use The HttpFoundation Component. It is easy to use in your Symfony application controller too. i.e
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/api/product/list", name="api_product_list", methods={"GET"})
* #param Request $request
* #return Response
*/
public function getList(Request $request): Response
{
$product = $request->get('product');
return $this->json(['product' => $product]);
}
More information about HTTPFoundation you already know.

Symfony 3.4: How to access bundle configuration in YAML

I'd like to ask if there is a way to access bundle configuration from YAML of that bundle.
Specifically, implementing Symfony\Component\Config\Definition\ConfigurationInterface I define that my bundle needs some configuration. User puts that configuration in their app/config/bundles/my_bundle.yml with all the keys I require for my bundle.
my_bundle:
magic_key: '42'
Now in my bundle I have Resources/config/services.yml in which I configure some services and I need magic_key for one of them.
Since I know magic_key is set (because of ConfigurationInterface) I now am able access that key in a class extending Symfony\Component\HttpKernel\DependencyInjection\Extension, get definition of particular service and set argument for that.
However I'd like to do this in Resources/config/services.yml located in my bundle instead of using and Extension class.
I've read at https://symfony.com/doc/3.4/service_container/expression_language.html that it should be possible using parameter or container functions, but I'm not able to do that.
The reasoning behind that is that I want to have configure my bundle services at single location - the YAML file - as opposed to current situation where it is split between YAML and Extension.php.
Is it indeed possible? What is the right syntax?
you need to add your configuration in parameters in your MyBundleExtension class like this:
public function load(array $configs, ContainerBuilder $container)
{
$container->setParameter('my_bundle', $config);
}
Then you can add "%my_bundle%" in service arguments.

How to override bundle templates and controllers (FOSUserBundle) without using bundle inheritance

In my Symfony 2.8 based project I use FOSUserBundle and extended it with a custom MyUserBundle using bundle inheritance.
When trying to update FOSUserBundle from 2.0.2 to 2.1.2 I came across several problems.
Since Bundle inheritance is deprecated in Symfony 3.4 and completely dropped in Symfony 4.0 I am now trying to achieve the same result without using bundle inheritance.
I found many information that shows, that bundle resources can be overridden by simply placing the files in the app/Resources/<BundleName>/.... Thus the FOSUserBundle templates could be overridden by placing them in app/Resources/FOSUserBundle/views/<template.html.twig>
While this will work, it does not deliver the same result as bundle inheritance. I can use my inherited bundle in different projects and thus reuse the same code over and over again. Using the app/Resources/<BundleName>/... solution would only work for the current project.
Question 1: How to override templates (and other resources) within a custom bundle which can be used in different projects rather than within a project specific app/Resources folder?
Question 2: How to override the behavior of controllers which to not offer event to to do so?
Currently I am using bundle inheritance to override the confirmedAction to send the user to a custom setup page rather than to the default page:
// Original FOSUserBundle code
public function confirmedAction(Request $request) {
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
return $this->render('#FOSUser/Registration/confirmed.html.twig', array(
'user' => $user,
'targetUrl' => $this->getTargetUrlFromSession($request->getSession()),
));
}
// MyUserBundle codes which should override the default behavior
public function confirmedAction(Request $request) {
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
// Send user to setup-page
return $this->redirectToRoute('app_user_setup');
}
How can I control the behavior in the same way and send the user to a setup page instead of to the default confirmation page without using bundle inheritance?
As far as I know FOSUserBundle does not provide an option to specify the confirmation target. Of course I could intercept the request to the confirmed-route using some request event listener and re-route it. But this would be quite hacky. Would this be the right solution to do this or is there a cleaner way to achieve the same?
Overriding resources within a bundle since Symfony 4.0 without bundle inheritance
About Controllers
Routes! is the way to achieve it now, "the first route found wins" so make sure to define the same route before the original one, with your custom controller. e.g.:
<route id="fos_user_registration_confirmed" path="/confirmed" methods="GET">
<default key="_controller">MyUserBundle\Controller\RegistrationController::confirmedAction</default>
</route>
Then, your routing file MyUserBundle/Resources/config/routing/registration.xml must be imported before the FOSUserBundle routing file. This way, your custom action will be executed instead of the original fos_user.registration.controller:confirmedAction.
About Templates
Again, "the first template found wins" so make sure your bundle views path is defined before the one in FOSUser Twig namespace. You can achieve it by creating a compiler pass:
// MyUserBundle/DependencyInjection/Compiler/TwigPass.php
$path = dirname(__DIR__, 2).'/Resources/views';
$twigFilesystemLoaderDefinition = $container->findDefinition('twig.loader.native_filesystem');
$twigFilesystemLoaderDefinition->addMethodCall('prependPath', array($path, 'FOSUser'));
Check the Twig loader paths by running bin/console debug:twig, it should look like this:
#FOSUser - vendor/acme/my-user-bundle/Resources/views
- vendor/friendsofsymfony/user-bundle/Resources/views
From here, you're able to override any template of FOSUserBundle like Registration/confirmed.html.twig when #FOSUser/Registration/confirmed.html.twig is called.

How to move slug to subdomain in Symfony3

I have a web-app where every User has his own personalized link
https://app.myapp.com/{uuid}/frontpage
Where uuid indicates User resource loaded from the database.
And now, the idea is to move /{uuid}/ to subdomain, so it should look as follows:
https://{uuid}.myapp.com/frontpage
While I have created wildcard DNS and can extract subdomain in Symfony's Controllers easily, the problem is now how to tell Symfony that uuid should be taken from subdomain now.
In Controllers I have routing defined as follows
/**
* #Route("/{uuid}/frontpage", name="frontpage")
* #ParamConverter("user", class="AppBundle:User", converter="converter.user")
* #Template("FrontPage.html.twig")
*/
public function indexAction(Request $request, User $user)
{
}
I would like to avoid rewriting all Controllers and strip out /{uuid}/ part because I have hundreds of Controllers defined like that.
Is there a way to manage this maybe via Listeners?
With Symfony 3, you can match a route based on the host. And basically you can also "placeholderize" your subdomain and get the related value in your HttpFoundation\Request object (in your controller for instance).
Ex. :
mobile_homepage:
path: /
host: "{subdomain}.example.com"
defaults:
_controller: AppBundle:Main:mobileHomepage
subdomain: m
https://symfony.com/doc/current/routing/hostname_pattern.html

Admin area across various bundles

I'm relatively new to Symfony 2 and I'm working on an application which has an admin only login area located under domain.com/admin/home/ (which itself is a separate bundle called AdminBundle) allowing the admin to manage products
However the same application is also going to have a BlogBundle which will require an admin area to manage blog posts.
What is the best way to tackle this, is there a best practice for multiple admin areas?
It makes sense to keep the blog admin section within the BlogBundle for better re-usability/portability, however all of the login settings will be in the AdminBundle which seems wrong.
One way to do this is to use your security.yml file to secure a set of routes. For example;
access_control:
...
- { path: ^/admin, roles: [ROLE_ADMIN] }
Then when you create your actions you simply need to give them a route with an admin prefix to secure them against all users other than admin. My examples use annotation for routing but you can do the same with yml.
/**
* Edit a Blog entity.
*
* #Route("/admin/blog/{id}/edit", name="blog_edit")
* #Template()
*/
public function editAction()
{
...
Or you could secure every action in the controller by prefixing the class:
/**
* AdminBlog controller.
*
* #Route("/admin/blog")
*/
class AdminBlogController extends Controller
{
...
Then create another controller which handles displaying the blog posts to the end user and give them unsecured routes.
If you want to secure other areas in seperate bundles your can do it in the same way by have secure and unsecure controllers/ methods.actions

Resources