如何在不使用包继承的情况下覆盖包模板和控制器(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