Должно ли все быть в связке в Symfony 2.x?

Мне известны такие вопросы, когда люди склонны обсуждать общую концепцию Symfony 2 bundle.

Дело в том, что в конкретном приложении, таком как, например, приложение, похожее на твиттер, все ли должно быть внутри общего пакета, как говорят официальные документы?

Причина, по которой я спрашиваю об этом, заключается в том, что, когда мы разрабатываем приложения, мы вообще не хотим связывать наш код с какой-либо интегрированной средой полного стека.

Если я разрабатываю приложение на базе Symfony 2 и в какой-то момент я решаю, что Symfony 2 - не лучший выбор для продолжения разработки, это будет проблемой для меня?

Итак, общий вопрос: почему все, что нужно, - это хорошая вещь?

EDIT # 1

Почти год, так как я задал этот вопрос, я написал статью, чтобы поделиться своими знаниями по этой теме.

6 ответов

Решение

Я написал более подробный и обновленный пост в блоге на эту тему: 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
        ];
    }
}

Обратите внимание, что я использую свой https://github.com/elnur/ElnurAbstractControllerBundle для упрощения определения контроллеров как сервисов.

Последнее, что осталось, - сказать Symfony искать шаблоны без пакетов. Я делаю это, переопределяя службу guesser для шаблонов, но так как в 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' %}

Конечно, вы можете отделить ваше приложение. Просто разработайте его как библиотеку и интегрируйте в Symfony vendor/папка (либо с помощью deps или же composer.jsonВ зависимости от того, используете ли вы Symfony2.0 или Symfony2.1). Однако вам нужен как минимум один пакет, который действует как "внешний интерфейс" вашей библиотеки, где Symfony2 находит контроллер (и тому подобное).

Обычный дистрибутив Symfony может работать без каких-либо дополнительных (прикладных) пакетов, в зависимости от того, сколько функций вы хотите использовать в полной структуре стека.

Например, ваши контроллеры могут быть любыми вызываемыми, которые можно поместить в любое место в структуре вашего проекта, как только они будут автоматически загружены.

В файле определения маршрутизации вы можете использовать:

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), потому что они обнаруживаются только через пакеты.

Распределение symfony-light направлено на решение подобных проблем путем обнаружения всего, что обычно обнаруживается только через связки.

Вы можете использовать KnpRadBundle, который пытается упростить структуру проекта.

Другой подход заключается в использовании src/Company/Bundle/FrontendBundle например, для связок и src/Company/Stuff/Class.php для классов, которые не зависят от Symfony и могут быть повторно использованы вне рамок

Поскольку прошло уже 5 лет, вот еще несколько статей о Symfony Bundles.

  1. Что такое связки в Symfony? Ильтар ван дер Берг.

TLDR:

Вам нужно несколько пакетов в вашем приложении напрямую? Скорее всего нет. Вам лучше написать AppBundle, чтобы предотвратить спагетти зависимостей. Вы можете просто следовать лучшим практикам, и это будет работать нормально.

  1. Symfony: как связать, Тони Юберникель

TLDR:

Создайте только один пакет с именем AppBundle для логики вашего приложения. Один AppBundle - но, пожалуйста, не помещайте туда логику своего приложения!

Среда Symfony очень хороша для быстрого запуска проверки концепции, и весь код можно вводить в стандартном пакетном приложении в src/.

В этом комплекте вы можете структурировать свой код так, как хотите.

Если вы хотите использовать другие технологии для разработки вашего POC, вы можете легко перевести это, потому что вы не структурируете весь свой код в концепции пакета.

Для всей концепции вы не экстремизировали это. Связка это хорошо, но связка все и каждый день не хорошо.

Возможно, вы можете использовать Silex (микро-фреймворк Symfony) для разработки своего Proof of Concept для уменьшения влияния стороннего пакета.

Другие вопросы по тегам