编辑:Symfony 最佳实践回答了我的大部分问题。
我有几个关于我的 Symfony2 应用程序的问题。
它将有一个前端和一个后端,它们将使用一些通用代码(例如日期显示器、分页器、一些常用的模板等)。
因此,我创建了一个 FrontendBundle 和一个 BackendBundle,每个包含例如它们各自的布局。第一个问题:为前端和后端创建捆绑包(它们是甚至没有控制器的“通用”捆绑包)是一种好的做法吗?
第二个问题:我在一本食谱上读到,我不应该将布局放在捆绑中,而应该放在 app/Resources/views/ 目录中。我已经有一个 base.html.twig 文件,我想知道我是否也应该把我的布局放在那里,就像 frontend_layout.html.twig 文件一样?
我创建了一个名为 RootBundle 的包,其中包含我的应用程序在前端和后端所需的所有内容。这是一个好习惯吗?或者我应该为每个建议的功能创建一个专用的包,例如 PaginatorBundle、DateDisplayerBundle 等?听起来很奇怪,我有一个“杂项”包,其中包含我不知道该放在哪里的所有内容。你是怎么做到的?
Edit: the Symfony best practices answer most of my questions.
I have several questions concerning my Symfony2 app.
It will have a frontend and a backend, and they will use some common code (such as a date displayer, paginator, some templates often used, etc).
So, I created one FrontendBundle and one BackendBundle, each containing for example their respective layout. First question: is that good practice to create bundles for frontend and backend, which are "general" bundles which won't even have a controller?
Second question: I read on a cookbook that I should not put my layouts in bundles, but in the app/Resources/views/ directory. I already have a base.html.twig file in it, and I wonder if I should put my layouts in there too, like a frontend_layout.html.twig file?
I created a bundle named RootBundle, which would contain everything my app needs in frontend AND backend. Is that a good practice or not? Or I should create a dedicated bundle for each functionality proposed, such as a PaginatorBundle, a DateDisplayerBundle, etc? It sounds weird that I have one "miscellaneous" bundle containing everything I don't know where to put. How do you do that?
发布评论
评论(2)
新方法
自从我写这个答案几个月后,我的方法发生了变化,所以我与社区分享。这个答案仍然很受欢迎,可以引导新手采用我认为不再是最好的方法。所以...
现在我只有一个特定于应用程序的捆绑包,我将其称为
AppBundle
。旧方法存在几个问题,以下是其中的一些问题:创建大量捆绑包非常繁琐。您必须为每个新捆绑包创建一个捆绑包类和一堆标准文件夹然后激活它并注册它的路由和 DI 等等。
不必要的核心决策过程。有时您无法决定某个特定事物属于哪个捆绑包,因为它被多个捆绑包使用。当你花了半天时间最终决定把它放在哪里之后,你会发现在几天或几周内你将无法立即知道该在哪个包中查找该东西 -因为大多数时候,决定并不是基于纯粹的逻辑,你必须根据抛硬币或任何你用来获得更高权力来寻求帮助的方式来进行选择。
我过去建议使用
CommonBundle
来处理常见的东西,但这样做你将不得不进行大量不必要的重构,将东西移入和移出CommonBundle
基于以后有多少个包会使用这个东西。无论如何,特定于应用程序的捆绑包都是相互依赖的。当人们第一次遇到捆绑包的想法时,他们脑海中浮现的主要想法之一就是“耶!我会给我一堆可重复使用的包裹!”这个想法很棒,我并不反对。问题是特定于应用程序的捆绑包无论如何都不是可重用的——它们是相互依赖的。在这种情况下忘记重用。
不知道在哪里放置 Behat 功能和步骤定义。 此问题与之前的:你必须对每个捆绑重复相同的无脑动作,然后做出核心决定。
当我开始编写 Behat 功能时,我无法决定将大量功能和步骤定义放在哪里,因为它们一次属于多个包。将它们放入
CommonBundle
似乎更糟糕,因为这是我在其中查找这些内容的最后一个包。因此,我最终为此创建了FeatureBundle
。切换到单一捆绑包解决了所有这些问题。
我还看到有些人为所有实体提供了单独的捆绑包。我也不喜欢这种方法,实际上 建议将实体和其他非 Symfony2 特定内容排除在捆绑包之外。
再次注意,这种新方法适用于特定于应用程序的捆绑包。官方文档和其他地方充满了关于如何构建旨在与其他人共享并在众多项目中重用的捆绑包的很好的建议。 我也编写这种类型的捆绑包。但经过几个月的 Symfony2 项目工作后我发现,用于重用的捆绑包和特定于应用程序的捆绑包之间存在差异 - 一种方法并不适合所有情况。
当然,当您看到应用程序特定捆绑包中出现可重用的内容时,只需将其提取,将其放入单独的存储库中并作为供应商安装即可。
此外,我发现自己更积极地使用子命名空间作为对捆绑包进行逻辑分区的一种方式,而不是为此创建一堆捆绑包并经历所有这些麻烦。
旧方法
没有硬性规定或灵丹妙药,但我将分享我的做事方法——也许它会给您一两个见解。
首先,我没有像
FrontendBundle
和BackendBundle
这样的两个包罗万象的捆绑包。相反,我的包同时具有前端和后端控制器、视图等。因此,如果我从UserBundle
中删除除控制器和视图之外的所有内容,其结构将如下所示:其次,我有
CommonBundle
,我用于由多个包共享的内容:我的
app/Resources/views/base.html.twig
与 Symfony 标准发行版中的几乎相同:两者
CommonBundle/Resources/views/layout.html
和CommonBundle/Resources/views/Admin/layout.html
扩展app/Resources/views/base.html.twig
。其他捆绑包的模板扩展了这两种布局之一,具体取决于它们是用于前端还是后端。基本上,这就是我使用 三级继承方法的方式。因此,我会将您的日期显示器放入
CommonBundle
中。根据其复杂性,它可能只是一个模板、宏或Twig 扩展。分页是一个常见问题,因此我建议您使用现有 捆绑,而不是重新发明轮子 - 当然,如果它们适合您的需求。
是的,拥有没有控制器或视图等的捆绑包是完全可以的。
The new approach
After several months since I wrote this answer, my approach has changed, so I'm sharing it with the community. This answer is still pretty popular and can lead newcomers to the approach I don't think is the best one anymore. So...
Now I have only one app specific bundle and I call it
AppBundle
. There were several problems with the old approach and here are some of them:Creating a lot of bundles is tedious. You have to create a bundle class and a bunch of standard folders for each new bundle and then activate it and register its routes and DI and whatnot.
Unnecessary hardcore decision making process. Sometimes you just can't decide which bundle a particular thing belongs to because it's used by more than one bundle. And after you spend a half a day and finally make your hard decision on where to put it, you'll find that in a couple of days or weeks you won't be able to tell right away which bundle to look that thing in — because most of the times the decision wasn't based on pure logic and you had to choose based on a coin toss or whatever means you use to bring higher powers for help.
I suggested using
CommonBundle
for common stuff in the past but doing that you'll have to do a lot of unnecessary refactorings moving a thing to and fromCommonBundle
based on how many or few bundles will use that thing later.App specific bundles are interdependent anyway. When people meet the idea of bundles for the first time, one of the main thought that goes through their minds is something like “Yay! I'll have me a bunch of reusable bundles!” That idea is great and I have nothing against it; the problem is that app specific bundles are not that reusable anyway — there are interdependent. Forget about reuse in this case.
No idea where to put Behat features and step definitions. This problem is related to the previous ones: you have to repeat the same brainless motions for each bundle and then make hardcore decisions.
When I started writing Behat features, I just couldn't decide where to put a lot of features and step definitions because they belonged to several bundles at a time. Putting them into
CommonBundle
seemed to be even worse, because that's the last bundle I would look for that stuff in. So, I ended up creatingFeatureBundle
for that.Switching to a single bundle solved all these problems.
I've also seen some people having a separate bundle for, say, all the entities. I don't like this approach neither and actually suggest keeping entities and other non Symfony2 specific stuff out of the bundles.
Note again that this new approach applies to app specific bundles. Official docs and other places are full of great advice on how to structure bundles intended to be shared with others and reused across numerous projects. I write bundles of this type as well. But what I've found out after months of working on Symfony2 projects is that there is a difference between the bundles intended for reuse and the app specific ones — one approach doesn't fit all.
And, of course, when you see something reusable emerging in your app specific bundle, just extract it, put it in a separate repo and install as a vendor.
Also I've found myself using subnamespaces much more actively as a way to partition the bundle logically — instead of creating a bunch of bundles for that and going through all those troubles.
The old approach
There are no hard and fast rules or silver bullets, but I'll share my approach of doing things — maybe it will give you an insight or two.
First of all, I don't have two all-encompassing bundles like
FrontendBundle
andBackendBundle
. Instead, my bundles have both frontend and backend controllers, views, etc. So, if I strip everything from myUserBundle
except for controllers and views, its structure would look like this:Second, I have
CommonBundle
which I use for stuff shared by several bundles:My
app/Resources/views/base.html.twig
is almost the same as it comes with Symfony Standard distribution:Both
CommonBundle/Resources/views/layout.html
andCommonBundle/Resources/views/Admin/layout.html
extendapp/Resources/views/base.html.twig
. Other bundles' templates extend one of these two layouts, depending on whether they are for frontend or backend. Basically, this is how I'm using the Three-level Inheritance approach.So, I'd put your date displayer into
CommonBundle
. Depending on its complexity it could be just a template, a macro or a Twig extension.Pagination is a common problem, so I suggest you to use one of the existing bundles instead of reinventing the wheel — if they suite your needs, of course.
And yes, it's perfectly okay to have bundles without controllers or views, etc.
我建议创建 DateDisplayerBundle 和 PaginatorBundle,而不是将它们的相关代码放在更通用的包中。这样做的原因有几个:
没有硬性规定说捆绑包必须有控制器。捆绑包可以包含业务逻辑、模板、控制器和配置的任意组合,但对可以在其中存储的内容没有限制。
另一方面,如果您的功能不是很复杂,则可能根本不保证将其包含在捆绑包中。在这种情况下,您可以在
/vendor
中为其创建一个库。 Symfony 以这种方式使用了许多库(例如,请参阅 Monolog 和 Doctrine。)至于你的第二个问题,我认为将布局保留在
app\Resources\views
中的原因是因为它是一个方便您跟踪所有布局。当您的项目包含许多包时,您可能会忘记某个布局的位置。但如果您将它们全部集中在一个集中位置,您将始终确切地知道该去哪里查找。与 Symfony2 中的许多内容一样,这并不是一成不变的规则。您可以轻松地将布局存储在一个包中,但我不认为这是推荐的做法。至于您关于一般根包的问题,我想说在大多数情况下您应该避免在一个包中硬塞一堆不同的功能。请参阅我之前关于保持捆绑包的具体性的观点。当我开始使用 Symfony2 进行开发时,我在确定哪些代码应该放在哪个包中时遇到了一些困难。这不是我习惯思考编程的方式。但最终你会开始看到拼图的各个部分是如何组合的,这使得确定捆绑结构变得更容易。
I suggest creating a DateDisplayerBundle and a PaginatorBundle instead of putting their related code in a more general bundle. There are a few reasons for this:
There's no hard rule saying that bundles must have controllers. Bundles can have any mix of business logic, templates, controllers, and configuration, but there's no restriction on what you can store in them.
On the other hand, if your functionality is not very complex, it may not warrant being contained within a bundle at all. In this case you could create a library in
/vendor
for it. Symfony makes use of a number of libraries in this fashion (see Monolog and Doctrine for example.)As for your second question, I think the reason for keeping layouts in
app\Resources\views
is because it's a convenient way for you to keep track of all your layouts. When you have a project that has many bundles, you might lose track of where a certain layout is. But if you keep them all within one centralized location, you'll always know exactly where to look. As with many things in Symfony2, this isn't a rule that's set in stone. You could easily store your layouts in a bundle, but I don't think it's the recommended practice.As for your question about your general Root bundle, I would say in most cases you should avoid shoehorning a bunch of different features in one bundle. See my earlier points about keeping your bundles specific. When I started developing with Symfony2, I had some trouble determining what code should go in what bundle. It's not how I was used to thinking about programming. But eventually you start to see how the individual pieces of the puzzle fit, and that makes determining the bundle structure easier.