Symfony 2.x中的所有东西都应该是一个捆绑吗?

我意识到这样的问题,人们倾向于讨论Symfony 2的一般概念。

问题是,在一个特定的应用程序中,比如像一个类似twitter的应用程序,是否应该像真正的官方文档一样,真正在一个通用的bundle中?

我问这个问题的原因是,当我们开发应用程序时,通常我们不想将代码高度耦合到一些全栈胶水框架。

如果我开发一个基于Symfony 2的应用程序,并且在某种程度上,我决定Symfony 2并不是真正保持开发进展的最佳select,那对我来说会是一个问题吗?

所以一般的问题是:为什么一切都成为一件好事?

编辑#1

我问这个问题差不多一年了,我写了一篇文章来分享我对这个话题的认识。

我已经写了一个更彻底和更新的博客文章关于这个主题: http : //elnur.pro/symfony-without-bundles/


不,不是所有的东西都要捆绑在一起。 你可以有这样的结构:

  • src/Vendor/Model – 对于模型,
  • src/Vendor/Controller – 对于控制器,
  • src/Vendor/Service – 对于服务,
  • src/Vendor/Bundle – 对于捆绑包,比如src/Vendor/Bundle/AppBundle
  • 等等

这样,你只需AppBundle放在Symfony2特有的东西中即可。 如果您决定稍后切换到另一个框架,您将摆脱Bundle命名空间并将其replace为所选框架的东西。

请注意,我在这里build议的是应用程序特定的代码。 对于可重用的捆绑包,我仍然build议使用最佳实践 。

保持实体不受束

为了让实体保留在任何包之外的src/Vendor/Model中,我已经将config.ymldoctrine部分从

 doctrine: # ... orm: # ... auto_mapping: true 

 doctrine: # ... orm: # ... mappings: model: type: annotation dir: %kernel.root_dir%/../src/Vendor/Model prefix: Vendor\Model alias: Model is_bundle: false 

实体的名称 – 从Doctrine存储库访问 – 在这种情况下,从Model开始,例如Model:User

您可以使用子名称空间将相关实体组合在一起,例如src/Vendor/User/Group.php 。 在这种情况下,实体的名称是Model:User\Group

保持控制器的捆绑

首先,你需要告诉JMSDiExtraBundle通过将这个添加到config.yml来扫描src文件夹中的服务:

 jms_di_extra: locations: directories: %kernel.root_dir%/../src 

然后, 将控制器定义为服务 ,并将其置于Controller名称空间下:

 <?php namespace Vendor\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use JMS\DiExtraBundle\Annotation\Service; use JMS\DiExtraBundle\Annotation\InjectParams; use JMS\SecurityExtraBundle\Annotation\Secure; use Elnur\AbstractControllerBundle\AbstractController; use Vendor\Service\UserService; use Vendor\Model\User; /** * @Service("user_controller", parent="elnur.controller.abstract") * @Route(service="user_controller") */ class UserController extends AbstractController { /** * @var UserService */ private $userService; /** * @InjectParams * * @param UserService $userService */ public function __construct(UserService $userService) { $this->userService = $userService; } /** * @Route("/user/add", name="user.add") * @Template * @Secure("ROLE_ADMIN") * * @param Request $request * @return array */ public function addAction(Request $request) { $user = new User; $form = $this->formFactory->create('user', $user); if ($request->getMethod() == 'POST') { $form->bind($request); if ($form->isValid()) { $this->userService->save($user); $request->getSession()->getFlashBag()->add('success', 'user.add.success'); return new RedirectResponse($this->router->generate('user.list')); } } return ['form' => $form->createView()]; } /** * @Route("/user/profile", name="user.profile") * @Template * @Secure("ROLE_USER") * * @param Request $request * @return array */ public function profileAction(Request $request) { $user = $this->getCurrentUser(); $form = $this->formFactory->create('user_profile', $user); if ($request->getMethod() == 'POST') { $form->bind($request); if ($form->isValid()) { $this->userService->save($user); $request->getSession()->getFlashBag()->add('success', 'user.profile.edit.success'); return new RedirectResponse($this->router->generate('user.view', [ 'username' => $user->getUsername() ])); } } return [ 'form' => $form->createView(), 'user' => $user ]; } } 

请注意,我正在使用我的ElnurAbstractControllerBundle来简化将控制器定义为服务。

剩下的最后一件事就是告诉Symfony无束查找模板。 我通过覆盖模板猜测服务来做到这一点,但是由于Symfony 2.0和2.1的方法不同,我为它们提供了版本。

重写Symfony 2.1+模板猜测器

我已经创build了一个捆绑包 ,为你做。

覆盖Symfony 2.0模板监听器

首先,定义类:

 <?php namespace Vendor\Listener; use InvalidArgumentException; use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Bundle\Bundle; use Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener as FrameworkExtraTemplateListener; use JMS\DiExtraBundle\Annotation\Service; class TemplateListener extends FrameworkExtraTemplateListener { /** * @param array $controller * @param Request $request * @param string $engine * @throws InvalidArgumentException * @return TemplateReference */ public function guessTemplateName($controller, Request $request, $engine = 'twig') { if (!preg_match('/Controller\\\(.+)Controller$/', get_class($controller[0]), $matchController)) { throw new InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0]))); } if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) { throw new InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1])); } $bundle = $this->getBundleForClass(get_class($controller[0])); return new TemplateReference( $bundle ? $bundle->getName() : null, $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine ); } /** * @param string $class * @return Bundle */ protected function getBundleForClass($class) { try { return parent::getBundleForClass($class); } catch (InvalidArgumentException $e) { return null; } } } 

然后告诉Symfony使用它添加到config.yml

 parameters: jms_di_extra.template_listener.class: Vendor\Listener\TemplateListener 

使用无捆绑的模板

现在,您可以使用捆绑模板。 把它们保存在app/Resources/views文件夹下。 例如,以上示例控制器中的这两个操作的模板位于:

  • app/Resources/views/User/add.html.twig
  • app/Resources/views/User/profile.html.twig

当引用一个模板时,只需要省略捆绑部分:

 {% include ':Controller:view.html.twig' %} 

当然,你可以解耦你的应用程序。 把它作为一个库来开发,并把它集成到symfony vendor/夹中(通过使用deps或者composer.json ,取决于你使用Symfony2.0还是Symfony2.1)。 但是,至less需要一个bundle,充当库的“前端”,Symfony2可以find控制器(等等)。

通常的symfony发行版可以在没有任何额外的(应用程序)包的情况下工作,具体取决于您想从完整的堆栈框架使用多lessfunction。

例如,您的控制器可以是任何可调用的,只要它们是自动加载的,就可以放在项目结构中的任何地方。

在路由定义文件中,您可以使用:

 test: pattern: /test defaults: { _controller: Controller\Test::test } 

它可以是任何普通的旧的php对象,只有绑定到框架的事实,它必须返回一个Symfony\Component\HttpFoundation\Response对象。

你的树枝模板(或其他)可以像app/Resources/views/template.html.twig一样放置,并可以使用::template.html.twig逻辑名来渲染。

所有的DI服务都可以在app / config / config.yml中定义(或者从app/config/services.yml导入),所有的服务类也可以是任何普通的旧的php对象。

所有这些都是由symfony全栈框架默认提供的。

如果你想使用翻译文件(如xliff),那么你会遇到问题,因为它们是通过捆绑发现的。

发光分布的目的是通过发现通常只能通过捆绑发现的一切来解决这些问题。

你可以使用KnpRadBundle ,它试图简化项目结构。

另一种方法是使用src/Company/Bundle/FrontendBundle作为bundle和src/Company/Stuff/Class.php中的类,它们是独立于symfony的类,可以在框架之外重用

由于已经过了5年了,这里还有更多关于Symfony Bundles的文章。

  1. Symfony中的Bundles是什么? 通过Iltar van der Berg。

TLDR:

你直接在你的应用程序中需要多个包吗? 很可能不是。 你最好写一个AppBundle来防止依赖的意大利面条。 你可以简单地遵循最佳实践 ,它会正常工作。

  1. Symfony:如何捆绑 Toni Uebernickel。

TLDR:

为应用程序逻辑创build一个名为AppBundle的包。 一个AppBundle – 但请不要把你的应用逻辑放在那里!

Symfony框架非常适合快速启动概念validation,所有代码都可以在src /

在这个包中,你可以根据需要构造你的代码。

如果你想使用其他技术来开发你的POC,你可以很容易地翻译它,因为你并没有把所有的代码都捆绑在一起。

对于所有的概念,你都不要过分强调这一点。 捆绑是好的,但捆绑一切,每天都不好。

也许你可以使用Silex(Symfony微框架)来开发你的概念certificate,以减less捆绑第三方的影响。