小编典典

Symfony 2.x 中的一切真的应该是一个捆绑包吗?

all

问题是,在一个特定的应用程序中,例如,一个类似推特的应用程序,是否真的应该像官方文档所说的那样,所有东西都在一个通用包中?

我问这个的原因是因为当我们开发应用程序时,一般来说,我们不希望将我们的代码高度耦合到一些全栈胶水框架。

如果我开发了一个基于 Symfony 2 的应用程序,并且在某个时候,我认为 Symfony 2 并不是 保持开发 的最佳选择,那对我来说会是个问题吗?

所以一般的问题是:为什么所有东西都是一个捆绑包是一件好事?

编辑#1

自从我提出这个问题以来已经快一年了,我写了一篇文章来分享我对这个主题的了解。


阅读 74

收藏
2022-06-27

共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命名空间并将其替换为所选框架的东西。

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

将实体排除在包之外

为了将实体保留在src/Vendor/Model任何捆绑包之外,我已将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.

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

让控制器远离捆绑包

首先,您需要告诉JMSDiExtraBundle扫描src文件夹中的服务,方法是将其添加到config.yml

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+ 模板猜测器

我已经为你创建了一个

覆盖 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' %}
2022-06-27