【问题标题】:Symfony2 conceptual issue: general bundles vs. specific onesSymfony2 概念问题:一般捆绑包与特定捆绑包
【发布时间】:2023-03-31 12:30:02
【问题描述】:

编辑:Symfony best practices 回答了我的大部分问题。

我有几个关于我的 Symfony2 应用程序的问题。

它将有前端和后端,它们会使用一些通用代码(例如日期显示器、分页器、一些经常使用的模板等)。

所以,我创建了一个 FrontendBundle 和一个 BackendBundle,每个都包含例如它们各自的布局。第一个问题:为前端和后端创建捆绑包是一种好的做法,它们是“通用”捆绑包,甚至没有控制器?

第二个问题:我在一本食谱上读到,我不应该将我的布局放在包中,而是放在 app/Resources/views/ 目录中。我已经有一个 base.html.twig 文件,我想知道我是否应该把我的布局也放在那里,比如 frontend_layout.html.twig 文件?

我创建了一个名为 RootBundle 的包,其中包含我的应用在前端和后端所需的一切。这是一个好习惯吗?或者我应该为每个提议的功能创建一个专用包,例如 PaginatorBundle、DateDisplayerBundle 等?我有一个包含所有我不知道放在哪里的“杂项”捆绑包,这听起来很奇怪。你是怎么做到的?

【问题讨论】:

  • @elnur 为什么要加sf2.1标签?
  • 因为它与 Symfony 2.1 的关系不亚于 Symfony 2.0。直到the Symfony tags mess is fixed,我觉得最好还是保持这样。
  • 我同意,问题不是 Symfony2.0 特有的

标签: php symfony bundle


【解决方案1】:

新方法

自从我写了这个答案几个月后,我的方法发生了变化,所以我将它与社区分享。这个答案仍然很受欢迎,并且可以引导新手采用我认为不再是最好的方法。所以...

现在我只有 一个 应用程序特定包,我称之为AppBundle。旧方法存在一些问题,以下是其中的一些:

  • 创建很多包很乏味。你必须为每个新包创建一个包类和一堆标准文件夹,然后激活它并注册它的路由和 DI 等等。 p>

  • 不必要的核心决策过程。 有时您只是无法确定特定事物属于哪个捆绑包,因为它被多个捆绑包使用。在你花了半天时间,最终做出艰难的决定后,你会发现在几天或几周后,你将无法立即确定该放在哪个包裹中——因为大多数时候这个决定不是基于纯粹的逻辑,你必须根据抛硬币或任何你用来带来更高权力寻求帮助的方式来选择。

    我过去曾建议将CommonBundle 用于常见的东西,但这样做你将不得不做很多不必要的重构,将一个东西移入和移出CommonBundle,这取决于以后有多少或几个包会使用那个东西.

  • 无论如何,特定于应用程序的捆绑包是相互依赖的。当人们第一次遇到捆绑包的想法时,他们脑海中的主要想法之一就是“耶!我会给我一堆可重复使用的捆绑包!”这个想法很棒,我不反对。问题是 app specific bundles 无论如何都不是可重用的——它们是相互依赖的。忘记在这种情况下的重用。

  • 不知道在哪里放置Behat 功能和步骤定义。 这个问题与前面的问题有关:您必须为每个捆绑包重复相同的无脑动作,然后做出核心决定。

    当我开始编写 Behat 功能时,我只是无法决定将大量功能和步骤定义放在哪里,因为它们一次属于多个包。将它们放入 CommonBundle 似乎更糟糕,因为这是我在其中寻找这些东西的最后一个包。所以,我最终为此创建了 FeatureBundle

切换到单个捆绑包解决了所有这些问题。

我还看到有些人为所有实体提供单独的捆绑包。我也不喜欢这种方法,实际上suggest keeping entities and other non Symfony2 specific stuff out of the bundles

再次注意,这种新方法适用于应用程序特定包。官方文档和其他地方充满了关于如何构建旨在与他人共享并在众多项目中重用的捆绑包的很好的建议。 I write bundles of this type 也是。但是,经过数月的 Symfony2 项目工作后,我发现用于重用的包和特定于应用程序的包之间存在差异——一种方法并不适合所有。

当然,当您在特定应用的捆绑包中看到可重复使用的内容时,只需将其提取出来,放入单独的存储库并作为供应商安装即可。

此外,我发现自己更积极地使用子命名空间作为对包进行逻辑分区的一种方式——而不是为此创建一堆包并经历所有这些麻烦。

旧方法

没有硬性规定或灵丹妙药,但我会分享我的做事方法——也许它会给你一两个见解。

首先,我没有像FrontendBundleBackendBundle 这样的包罗万象的包。相反,我的包同时具有前端和后端控制器、视图等。因此,如果我从 UserBundle 中剥离除控制器和视图之外的所有内容,其结构将如下所示:

UserBundle
├── Controller
│   ├── Admin
│   │   └── UserController.php
│   └── UserController.php
├── Resources
│   └── views
│       ├── Admin
│       │   └── User
│       │       ├── add.html.twig
│       │       ├── delete.html.twig
│       │       ├── edit.html.twig
│       │       ├── form.html.twig
│       │       └── index.html.twig
│       └── User
│           ├── edit.html.twig
│           ├── sign-in.html.twig
│           ├── sign-up.html.twig
│           └── view.html.twig
└── UserBundle.php

其次,我有 CommonBundle 用于多个捆绑包共享的内容:

CommonBundle
├── Resources
│   ├── public
│   │   ├── css
│   │   │   ├── admin.css
│   │   │   ├── common.css
│   │   │   └── public.css
│   │   └── img
│   │       ├── add.png
│   │       ├── delete.png
│   │       ├── edit.png
│   │       ├── error.png
│   │       ├── return.png
│   │       ├── success.png
│   │       └── upload.png
│   └── views
│       ├── Admin
│       │   └── layout.html.twig
│       └── layout.html.twig
└── CommonBundle.php

我的app/Resources/views/base.html.twig 几乎与 Symfony 标准发行版中的一样:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>{{ block('title') | striptags | raw }}</title>
        {% block stylesheets %}{% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{% endblock %}
    </body>
</html>

CommonBundle/Resources/views/layout.htmlCommonBundle/Resources/views/Admin/layout.html 都扩展了 app/Resources/views/base.html.twig。其他包的模板扩展了这两种布局之一,具体取决于它们是用于前端还是后端。基本上,这就是我使用Three-level Inheritance approach 的方式。

所以,我会将您的日期显示放入CommonBundle。根据其复杂性,它可能只是一个模板、macroTwig extension

分页是一个常见问题,因此我建议您使用existing bundles 之一,而不是重新发明轮子——当然,如果它们满足您的需求的话。

是的,没有控制器或视图等的捆绑包是完全可以的。

【讨论】:

  • 我认为CommonBundle 的另一个好名字可能是&lt;insert your project's name here&gt;Bundle,因为它应该包含所有特定于你的项目并且你认为你不会重复使用的东西任何地方。
  • 我非常喜欢你的方法,但我更喜欢为 admin 和 main 提供不同的 bundle(在不同的 bundle 中有不同的控制器和视图)。主要原因是管理部分通常依赖于主部分(服务、实体等),但主部分不依赖于管理部分。主包中的代码更少:)
  • 阅读您的更新方法后,我不知道赞成票是针对旧的还是新的:D
  • 现在甚至是 Symfony 最佳实践的一部分:symfony.com/doc/master/best_practices/…
【解决方案2】:

我建议创建一个 DateDisplayerBundle 和一个 PaginatorBundle,而不是将它们的相关代码放在一个更通用的包中。这有几个原因:

  • 每个bundle的作用很明确,你知道你的代码在哪里。
  • 当您拥有单独的捆绑包时,在不同项目之间共享功能片段(日期显示器、分页器)会更简单,而不是您可能需要修剪一个通用捆绑包。

没有硬性规定说捆绑包必须有控制器。 Bundles 可以包含业务逻辑、模板、控制器和配置的任意组合,但对您可以在其中存储的内容没有任何限制。

另一方面,如果您的功能不是很复杂,它可能根本不保证包含在捆绑包中。在这种情况下,您可以在/vendor 中为它创建一个库。 Symfony 以这种方式使用了许多库(例如,参见 Monolog 和 Doctrine。)

关于您的第二个问题,我认为将布局保留在app\Resources\views 中的原因是因为它是您跟踪所有布局的便捷方式。当您有一个包含许多捆绑包的项目时,您可能会忘记某个布局的位置。但是,如果您将它们全部保存在一个集中位置,您将始终准确地知道在哪里寻找。与 Symfony2 中的许多东西一样,这不是一成不变的规则。您可以轻松地将布局存储在一个包中,但我不认为这是推荐的做法。

至于您关于一般 Root 捆绑包的问题,​​我想说的是,在大多数情况下,您应该避免在一个捆绑包中硬塞一堆不同的功能。请参阅我之前关于保持捆绑包具体的观点。当我开始使用 Symfony2 进行开发时,我很难确定哪些代码应该放在哪个包中。这不是我习惯于思考编程的方式。但最终你会开始看到拼图的各个部分是如何匹配的,这使得确定捆绑包结构变得更加容易。

【讨论】:

  • 您的一些观点是好的,但我强烈不同意您将某些内容放入vendor 文件夹的建议。这个文件夹应该在你的仓库中被忽略,它正在被创建并填充bin/vendors 脚本。详情here
  • @elnur 我发现 vendor/ 文件夹非常有用。有时我在那里创建自己的可重用库,上传到我选择的 VCS 主机(bitbucket),并通过 composer 在我需要它们的任何项目中使用它们。编辑:刚才我注意到这个线程有多老了。
猜你喜欢
  • 1970-01-01
  • 2019-06-27
  • 1970-01-01
  • 1970-01-01
  • 2012-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多