Должно ли все быть в связке в Symfony 2.x? - PullRequest
200 голосов
/ 03 апреля 2012

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

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

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

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

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

EDIT # 1

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

Ответы [ 6 ]

217 голосов
/ 04 апреля 2012

Я написал более подробное и обновленное сообщение в блоге на эту тему: 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 искать шаблоны без пакетов. Я делаю это, переопределяя службу 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' %}
20 голосов
/ 03 апреля 2012

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

11 голосов
/ 15 июня 2012

Обычный дистрибутив 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 голосов
/ 23 марта 2017

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

  1. Что такое Пакеты в Symfony? Ильтара ван дер Берга.

TLDR:

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

Symfony: как связать от Toni Uebernickel.

TLDR:

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

5 голосов
/ 03 апреля 2012

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

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

0 голосов
/ 27 декабря 2015

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

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

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

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

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

...