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

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

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

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

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

Итак, общий вопрос: почему все, что является связкой, хорошо?

РЕДАКТИРОВАТЬ # 1

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

Ответы

Ответ 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+

Я создал bundle, который делает это для вас.

Переопределение прослушивателя шаблонов 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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

TL;DR:

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

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

TL;DR:

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

Ответ 6

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

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

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

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

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