问题是,在一个特定的应用程序中,例如,一个类似推特的应用程序,是否真的应该像官方文档所说的那样,所有东西都在一个通用包中?
我问这个的原因是因为当我们开发应用程序时,一般来说,我们不希望将我们的代码高度耦合到一些全栈胶水框架。
如果我开发了一个基于 Symfony 2 的应用程序,并且在某个时候,我认为 Symfony 2 并不是 保持开发 的最佳选择,那对我来说会是个问题吗?
所以一般的问题是:为什么所有东西都是一个捆绑包是一件好事?
编辑#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命名空间并将其替换为所选框架的东西。
AppBundle
Bundle
请注意,我在这里建议的是针对 应用程序 特定的代码。对于可重复使用的捆绑包,我仍然建议使用最佳实践。
为了将实体保留在src/Vendor/Model任何捆绑包之外,我已将doctrine部分更改config.yml为
doctrine
config.yml
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.
Model
Model:User
您可以使用子命名空间将相关实体组合在一起,例如src/Vendor/User/Group.php. 在这种情况下,实体的名称是Model:User\Group。
src/Vendor/User/Group.php
Model:User\Group
首先,您需要告诉JMSDiExtraBundle扫描src文件夹中的服务,方法是将其添加到config.yml:
src
jms_di_extra: locations: directories: %kernel.root_dir%/../src
然后将控制器定义为服务并将它们放在Controller命名空间下:
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 之间的方法不同,我为它们提供了两个版本。
我已经为你创建了一个包。
首先,定义类:
<?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
app/Resources/views/User/add.html.twig
app/Resources/views/User/profile.html.twig
引用模板时,只需省略捆绑部分:
{% include ':Controller:view.html.twig' %}