【问题标题】:Customize the Location and Order that Symfony auto loads classes, controllers, templates, files, etc自定义 Symfony 自动加载类、控制器、模板、文件等的位置和顺序
【发布时间】:2014-12-31 01:34:45
【问题描述】:

我正在构建一个托管 CMS,它使用平台的核心文件,但允许我们在必要时为客户端创建自定义文件,而不是核心文件。所以我的核心 Symfony 兼容代码位于这样的目录中:

// root directory
/var/www/core/cms/ 
// symfony app directory
/var/www/core/cms/app
// my cms bundle
/var/www/core/cms/src/Gutensite/CmsBundle
// web root
/var/www/core/web
etc.

这些遵循标准的命名约定和文件位置,因此 symfony 会在我需要时自动加载类。 Symfony 结构允许我根据需要覆盖供应商捆绑包,方法是将供应商捆绑包文件夹和文件放在我的 /app 或 /src/ 目录中。 See Docs.

"Bundle inheritance allows you to override any existing bundle in order to customize its 
 controllers, templates, or any of its files." 

但我的客户端文件夹将存在于不同的 vhost id 目录中,例如:

// the root of the client vhost directory
/var/www/vhosts/999/
// will reflect the same structure as the core app
/var/www/vhosts/999/src/

所以如果我有一个核心控制器叫做:

/var/www/core/cms/src/Gutensite/MenuBundle/Controller/MenuPrimaryController.php

我想为客户制作一个自定义版本,我会把它放在这里:

/var/www/vhosts/999/src/Gutensite/MenuBundle/Controller/MenuPrimaryController.php

我的主应用有一个 InitController,通常会像这样加载控制器:

$namespaceController = 'Gutensite\MenuBundle\Controller\MenuPrimaryController';
$contentController = new $namespaceController();

所以我想将该控制器的路径更改为不同的命名空间并让 Symfony 加载它,例如

// I think namespace can't have parts that start with numbers
$namespaceController = 'vhost999\src\Gutensite\MenuBundle\Controller\MenuPrimaryController';

现在,我的代码通过检查文件是否存在,然后要求该文件来手动执行此操作。然后new 命令加载正确的控制器。但如果可能的话,我想使用 symfony 的自动加载功能(并获得专家建议以最佳方式完成此操作)。

我想更好地了解如何自定义 symfony 查找的 ORDER 和 LOCATION 以覆盖捆绑包等。这样我就可以按以下顺序在以下位置查找:

1. Custom Directory (/var/www/vhosts/999/src/Gutensite/MenuBundle)
2. Template Directory (/var/www/core/cms/src/Gutensite/TemplateBundle/Controller/MyDesign/Gutensite/MenuBundle
3. Core Vendor Directory (/var/www/core/cms/src/Gutensite/MenuBundle)

更新--大部分已解决:

Cerad 的建议回答了这个问题的大部分内容,即当 Symfony 尝试加载控制器时,如何确定优先级。按照他的示例,我为所有以Gutensite 开头的命名空间注册了一个替代文件位置,方法是将其添加到app/autoload.php

$loader->add('Gutensite',   '/var/www/vhosts/1/src', true);

然后在我的控制器中,我通过打印确认它已注册:

print_r(ClassMapGenerator::createMap('/var/www/vhosts/1'));

返回:

Array
(
    [Gutensite\MenuBundle\Controller\MenuPrimaryController] => /var/www/vhosts/1/src/Gutensite/MenuBundle/Controller/MenuPrimaryController.php
)

这是正确的路径,并且确实加载了我的自定义控制器,而不是默认控制器。这正是我所需要的。

特定视图记录的替代命名空间

我还有一个稍微不同的用例需要解决。有时我会想为一个内容类型的所有实例创建一个自定义控制器(如上)。其他时候,我需要为特定页面创建一个特定实例(例如,查看 ID 35160)。

我尝试了很多不同的方法来组织它,以便我可以在命名空间或对象和文件名中包含 ID,但显然在如何使用数字、下划线和破折号方面存在很多限制 (见文末注释)。所以这是最终起作用的命名约定:

把自定义文件和通用控制器放在同一个文件夹下,文件名里放具体的视图ID号,不带下划线或破折号: /var/www/vhosts/1/src/Gutensite/MenuBundle/Controller/MenuPrimaryController35160.php

namespace Gutensite\MenuBundle\Controller;

class MenuPrimaryController35160
{
    public function indexAction()
    {
        echo "\n custom MenuPrimaryController for View ID 35160 in /var/www/vhosts/1/src";
    }
}

重要的命名约定说明:

我的尝试多次失败,因为我尝试使用数字、破折号和下划线作为命名空间路径、对象名称或文件名的一部分,但它们都以不同的方式失败。

  1. 根据comments on php.net,命名空间不能包含仅是数字的部分,例如foo/1/bar/ 会失败,但 foo/bar1 会工作。

  2. 自动加载中使用的下划线PSR-0 specs 将被转换为目录分隔符,例如foo/bar/class_1 将映射到 foo/bar/class/1 而不是名为 class_1 的文件。

  3. 对象名称不应包含“-”等特殊字符。

剩下的问题:修改控制器中的自动加载

我能否稍后在控制器中修改自动加载位置(而不是在上面使用的app/autoload.php 方法中)。我需要为 Gutensite 命名空间注册另一个路径以获取替代文件位置,并且它需要指向正确的模板文件夹,但是直到我在主控制器中加载站点信息之后,我才知道它们使用的是哪个模板.

I've created another question for this issue.

【问题讨论】:

    标签: symfony


    【解决方案1】:

    您可能会注意到 web/app.php 会加载 app/bootstrap.php.cache,而后者又会加载 app/autoload.php。

    一个典型的 app/autoload.php 可能是:

    $loader = require __DIR__.'/../vendor/autoload.php';
    
    $loader->add('Cerad',   __DIR__  . '/../../cerad2/src', true);
    
    AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
    
    return $loader;
    

    所以 Symfony 2 实际上使用了 composer 生成的 autoloaded。您可能不想弄乱作曲家的东西,尽管值得查看 vendor/composer/ClassLoader.php 以查看可用的功能。

    然而,加载器有一个 add 方法,可以让你调整一些东西。请注意,在我的示例中, true 作为第三个参数传递。这意味着将新路径添加到现有路径列表中。所以命名空间为 Cerad 的类将首先在 cerad2/src 目录下查找,然后是 composer 说的任何内容。

    我没有尝试使用数字作为目录,但我看不出 /var/www/vhosts/999 不能用作命名空间路径的任何原因。所以只需调整 app/autoload.php 就可以了。

    【讨论】:

    • 2.在我确定该站点属于哪个虚拟主机之后,有没有办法稍后在主控制器中修改加载程序?还是必须在app/autoload.php中完成,以后不能修改?
    • 您首先要了解自动加载:php.net/manual/en/language.oop5.autoload.php。然后阅读我的答案部分,我声明作曲家会自动加载。还可以考虑重新考虑您的整个定制方法。
    • 顺便说一句,我的大部分麻烦是由于命名空间路径中的数字和特殊字符。感谢您的时间。我添加了一个关于 how to add new namespace to auto loader from conteroller 的替代问题
    猜你喜欢
    • 2012-12-28
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 2018-07-28
    • 2012-10-07
    • 2010-11-15
    • 2019-06-06
    • 1970-01-01
    相关资源
    最近更新 更多