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

205

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

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

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

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

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

EDIT # 1

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

Даниэль Рибейро
источник
1
Это просто комментарий, а не ответ. Я лично думаю, что мы должны тщательно выбирать рамки перед началом проекта. Каждый фреймворк имеет свой собственный способ работы, поэтому он предоставляет инструменты для лучшей поддержки. Если нам так нравится, мы следуем. Есть и другие варианты. Мы не хотим использовать нож, чтобы резать дерево вместо пилы. Но это очень интересный вопрос, который вы задали :)
Anh Nguyen

Ответы:

219

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

Вы можете использовать subnamespaces для связанных групп лиц вместе, например, 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 искать шаблоны без пакетов. Я делаю это, переопределяя службу 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' %}
Эльнур Абдуррахимов
источник
2
Это действительно очень интересный подход. При этом я также могу разрабатывать реальные пакеты, которые содержат определенный набор функций, которые может использовать сообщество, не связывая мое приложение с самой структурой.
Даниэль Рибейро
57
Чтобы сделать код, которым вы делитесь с сообществом, также не связанным с Symfony2, вы можете поместить общий материал в библиотеку, а затем создать пакет, интегрирующий эту библиотеку с Symfony2.
Эльнур Абдуррахимов
9
Это интересная идея, если вы не полагаетесь ни на одну из команд генерации кода. generate:doctrine:crudнапример, ожидает, что сущность (= модель в случае Эльнура) будет в связке для работы.
Гек
2
При таком подходе есть ли способ восстановить функциональность интерфейса приложения / консоли CLI? Мне нравится идея держать мои модели вне любого пакета, но я бы хотел сохранить доступ к функциональности CLI.
Энди Бэйрд
3
Это должно быть помещено в связку :)
d0001
20

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

KingCrunch
источник
2
Из-за тега symfony-2.0я предполагаю, что вы используете текущую версию 2.0. В этом случае создайте git-репозиторий там, где вам нравится, и поместите в него все, что вы хотите разрабатывать независимо от Symfony. В вашем проекте symfony-update обновите ваш deps-file, как упомянуто здесь: symfony.com/doc/current/cookbook/workflow/… Затем просто создайте один (или несколько) набор (-ов) приложения (-ов php app/console generate:bundle) для специфических для symfony вещей.
KingCrunch
11

Обычный дистрибутив 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 направлено на решение подобных проблем путем обнаружения всего, что обычно обнаруживается только через связки.

Флориан Кляйн
источник
5

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

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

miguel_ibero
источник
Но тогда я бы связал приложение с KnpRadBundle ... Нет ли более легкого подхода в этом вопросе?
Даниэль Рибейро
1
Части, которые зависят от Symfony (контроллеры, модели, шаблоны и т. Д.), Всегда будут связаны с Symfony, поскольку вы используете его (расширение классов, использование помощников и т. Д.). Классы, которые работают в одиночку, будут в пространстве имен Company, и вы можете загрузить их, используя контейнер зависимостей. Эти классы могут быть независимыми от фреймворка.
miguel_ibero
1
Дело в том, что концепция Bundleнапрямую связана с публичным обменом. Когда я пишу какое-то приложение, я не хочу делиться своим кодом, за исключением тех частей, которые я намеренно создал как модули, управляемые сообществом. Я ошибся?
Даниэль Рибейро
Вам не нужно делиться связками. Думайте о пакете как о группе классов с некоторой конфигурацией. В каждом проекте вы можете иметь разные комплекты.
miguel_ibero
Вы должны прочитать книгу Symfony
miguel_ibero
5

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

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

TLDR:

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

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

TLDR:

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

Решат Белялов
источник
-2

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

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

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

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

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

DarkOmen
источник