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?
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.