如何在不使用包继承的情况下覆盖包模板和控制器(FOSUserBundle)
How to override bundle templates and controllers (FOSUserBundle) without using bundle inheritance
在我基于 Symfony 2.8
的项目中,我使用 FOSUserBundle
并使用捆绑继承通过自定义 MyUserBundle
扩展它。
在尝试将 FOSUserBundle
从 2.0.2 更新到 2.1.2 时,我遇到了几个问题。
由于 Bundle 继承在 Symfony 3.4
中被弃用并且在 Symfony 4.0
中完全被丢弃,我现在试图在不使用 bundle 继承的情况下获得相同的结果。
我发现很多信息表明,只需将文件放在 app/Resources/<BundleName>/...
中即可覆盖捆绑资源。因此,可以通过将 FOSUserBundle
模板放在 app/Resources/FOSUserBundle/views/<template.html.twig>
中来覆盖它们
虽然这会起作用,但它不会提供与包继承相同的结果。我可以在不同的项目中使用我继承的包,从而一遍又一遍地重用相同的代码。使用 app/Resources/<BundleName>/...
解决方案仅适用于当前项目。
问题 1:如何覆盖自定义包中的模板(和其他资源),该包可用于不同的项目而不是项目特定的 app/Resources
文件夹中?
问题 2:如何覆盖不提供事件的控制器的行为?
目前我正在使用包继承来覆盖 confirmedAction
以将用户发送到自定义设置页面而不是默认页面:
// 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');
}
如何在不使用包继承的情况下以相同的方式控制行为并将用户发送到设置页面而不是默认确认页面?
据我所知FOSUserBundle
没有提供指定确认目标的选项。当然,我可以使用一些请求事件侦听器拦截对已确认路由的请求并重新路由它。但这将是相当骇人听闻的。这是执行此操作的正确解决方案还是有更简洁的方法来实现相同的目的?
从 Symfony 4.0 开始在没有包继承的情况下覆盖包内的资源
关于控制器
路线!是现在实现它的方法,"the first route found wins" 所以请确保使用您的自定义控制器在原始路径之前定义相同的路径。例如:
<route id="fos_user_registration_confirmed" path="/confirmed" methods="GET">
<default key="_controller">MyUserBundle\Controller\RegistrationController::confirmedAction</default>
</route>
然后,您的路由文件 MyUserBundle/Resources/config/routing/registration.xml
必须在 FOSUserBundle
路由文件之前导入。这样,您的自定义操作将被执行,而不是原来的 fos_user.registration.controller:confirmedAction
.
关于模板
同样,"the first template found wins" 因此请确保您的包 views
路径定义在 FOSUser
Twig 命名空间中的路径之前。您可以通过创建 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'));
通过 运行 bin/console debug:twig
检查 Twig 加载器路径,它应该如下所示:
@FOSUser - vendor/acme/my-user-bundle/Resources/views
- vendor/friendsofsymfony/user-bundle/Resources/views
从这里开始,您可以在调用 @FOSUser/Registration/confirmed.html.twig
时覆盖 FOSUserBundle
的任何模板,例如 Registration/confirmed.html.twig
。
在我基于 Symfony 2.8
的项目中,我使用 FOSUserBundle
并使用捆绑继承通过自定义 MyUserBundle
扩展它。
在尝试将 FOSUserBundle
从 2.0.2 更新到 2.1.2 时,我遇到了几个问题。
由于 Bundle 继承在 Symfony 3.4
中被弃用并且在 Symfony 4.0
中完全被丢弃,我现在试图在不使用 bundle 继承的情况下获得相同的结果。
我发现很多信息表明,只需将文件放在 app/Resources/<BundleName>/...
中即可覆盖捆绑资源。因此,可以通过将 FOSUserBundle
模板放在 app/Resources/FOSUserBundle/views/<template.html.twig>
虽然这会起作用,但它不会提供与包继承相同的结果。我可以在不同的项目中使用我继承的包,从而一遍又一遍地重用相同的代码。使用 app/Resources/<BundleName>/...
解决方案仅适用于当前项目。
问题 1:如何覆盖自定义包中的模板(和其他资源),该包可用于不同的项目而不是项目特定的 app/Resources
文件夹中?
问题 2:如何覆盖不提供事件的控制器的行为?
目前我正在使用包继承来覆盖 confirmedAction
以将用户发送到自定义设置页面而不是默认页面:
// 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');
}
如何在不使用包继承的情况下以相同的方式控制行为并将用户发送到设置页面而不是默认确认页面?
据我所知FOSUserBundle
没有提供指定确认目标的选项。当然,我可以使用一些请求事件侦听器拦截对已确认路由的请求并重新路由它。但这将是相当骇人听闻的。这是执行此操作的正确解决方案还是有更简洁的方法来实现相同的目的?
从 Symfony 4.0 开始在没有包继承的情况下覆盖包内的资源
关于控制器
路线!是现在实现它的方法,"the first route found wins" 所以请确保使用您的自定义控制器在原始路径之前定义相同的路径。例如:
<route id="fos_user_registration_confirmed" path="/confirmed" methods="GET">
<default key="_controller">MyUserBundle\Controller\RegistrationController::confirmedAction</default>
</route>
然后,您的路由文件 MyUserBundle/Resources/config/routing/registration.xml
必须在 FOSUserBundle
路由文件之前导入。这样,您的自定义操作将被执行,而不是原来的 fos_user.registration.controller:confirmedAction
.
关于模板
同样,"the first template found wins" 因此请确保您的包 views
路径定义在 FOSUser
Twig 命名空间中的路径之前。您可以通过创建 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'));
通过 运行 bin/console debug:twig
检查 Twig 加载器路径,它应该如下所示:
@FOSUser - vendor/acme/my-user-bundle/Resources/views
- vendor/friendsofsymfony/user-bundle/Resources/views
从这里开始,您可以在调用 @FOSUser/Registration/confirmed.html.twig
时覆盖 FOSUserBundle
的任何模板,例如 Registration/confirmed.html.twig
。