[symfony] Symfony 2.x에서 모든 것이 실제로 번들이어야합니까?

나는 사람들이 일반적인 Symfony 2 번들 개념에 대해 토론하는 경향 있는 이와 같은 질문을 알고 있습니다.

예를 들어 트위터와 같은 응용 프로그램과 같은 특정 응용 프로그램에서는 공식 문서 와 같이 모든 것이 일반 번들 안에 있어야 합니까?

내가 묻는 이유는 일반적으로 응용 프로그램을 개발할 때 코드를 풀 스택 접착제 프레임 워크에 강력하게 연결하고 싶지 않기 때문입니다.

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입니다.

컨트롤러를 번들에서 제외

먼저 다음을 추가하여 JMSDiExtraBundlesrc 에 서비스 의 폴더 를 스캔하도록 지시 해야 합니다 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' %}


답변

물론 응용 프로그램을 분리 할 수 ​​있습니다. 그냥 라이브러리로 개발하고 심포니에 통합 vendor/합니다 (을 사용하여 중 – 폴더 deps또는 composer.json당신이 Symfony2.0 또는 Symfony2.1를 사용 어떠했는지를 따라). 그러나 라이브러리의 “프론트 엔드”역할을하는 하나 이상의 번들이 필요합니다. Symfony2는 컨트롤러 등을 찾습니다.


답변

풀 스택 프레임 워크에서 사용하려는 기능의 양에 따라 일반적인 심포니 배포판은 추가 (응용 프로그램) 번들없이 작동 할 수 있습니다.

예를 들어, 컨트롤러는 자동로드되는 즉시 프로젝트 구조의 어느 곳에 나 배치 할 수있는 호출 가능할 수 있습니다.

라우팅 정의 파일에서 다음을 사용할 수 있습니다.

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 full stack 프레임 워크에 의해 제공됩니다.

번역 파일 (xliff와 같은)은 번들을 통해서만 발견되기 때문에 번역 파일을 사용하고 싶을 때 문제가 발생합니다 .

심포니 빛 분포 목적은 보통 번들을 통해 발견 될 모든 것을 발견하여 이러한 문제 유형을 해결합니다.


답변

당신은 사용할 수 KnpRadBundle 프로젝트 구조를 단순화하려고합니다.

다른 접근 방식은 src/Company/Bundle/FrontendBundle예를 들어 번들 및 src/Company/Stuff/Class.php심포니 독립적이며 프레임 워크 외부에서 재사용 할 수있는 클래스에 사용하는 것입니다


답변

벌써 5 년이 지났으므로 Symfony Bundles에 대한 몇 가지 기사가 더 있습니다.

  1. Symfony의 번들은 무엇입니까? Iltar van der Berg.

TLDR :

애플리케이션에 여러 번들이 직접 필요합니까? 아마 아닐 것입니다. 의존성 스파게티를 방지하기 위해 AppBundle을 작성하는 것이 좋습니다. 모범 사례 를 따르기 만하면 제대로 작동합니다.

  1. 심포니 : Toni Uebernickel의 묶는 방법.

TLDR :

애플리케이션 로직에 대해 AppBundle이라는 하나의 번들 만 작성하십시오. 하나의 AppBundle-애플리케이션 로직을 넣지 마십시오!


답변

Symfony 프레임 워크는 개념 증명을 신속하게 시작하는 데 매우 적합하며 모든 코드는 src /의 기본 번들 응용 프로그램 내에 입력 할 수 있습니다.

이 번들에서 원하는대로 코드를 구성 할 수 있습니다.

POC 개발을 위해 다른 기술을 사용하려는 경우 모든 코드를 번들 개념으로 구성하지 않기 때문에 쉽게 번역 할 수 있습니다.

모든 개념에 대해 당신은 이것을 극단으로하지 않습니다. 번들은 좋지만 모든 것을 번들로 묶고 매일은 좋지 않습니다.

번들 번들의 영향을 줄이기 위해 개념 증명을 개발하기 위해 Silex (Symfony 마이크로 프레임 워크)를 사용할 수 있습니다.


답변