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


205

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

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

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

Symfony 2 기반 응용 프로그램을 개발하고 어느 시점에서 Symfony 2가 개발계속 진행 하는 최선의 선택이 아니라고 결정 하면 문제가 될까요?

일반적인 질문은 왜 모든 것이 번들이되는 것입니까?

편집 # 1

이 질문을 한 지 거의 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.

하위 네임 스페이스를 사용하여 관련 엔터티를 함께 그룹화 할 수 있습니다 (예 :) 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' %}

2
그것은 실제로 정말 흥미로운 접근법입니다. 이를 통해 애플리케이션을 프레임 워크 자체에 거의 연결하지 않고도 커뮤니티에서 사용할 수있는 특정 기능 세트가 포함 된 실제 번들을 개발할 수도 있습니다.
Daniel Ribeiro

57
Symfony2에 연결되지 않은 커뮤니티와 공유하는 코드를 만들기 위해 일반적인 내용을 라이브러리에 넣은 다음 해당 라이브러리를 Symfony2와 통합하는 번들을 작성할 수 있습니다.
Elnur Abdurrakhimov 2016

9
코드 생성 명령에 의존하지 않는 한 흥미로운 아이디어입니다. generate:doctrine:crud예를 들어 엔티티 (= elnur의 경우 모델)가 작동하기 위해 번들 안에있을 것으로 예상합니다.
geca

2
이 방법을 사용하면 CLI 앱 / 콘솔 인터페이스의 기능을 다시 얻을 수있는 방법이 있습니까? 모델을 번들 외부에 두는 아이디어를 좋아하지만 CLI 기능에 대한 액세스 권한을 유지하고 싶습니다.
Andy Baird

3
묶음에 넣어야합니다 :)
d0001

20

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


2
태그 때문에 symfony-2.0현재 2.0 버전을 사용한다고 가정합니다. 이 경우 원하는 곳에 git 저장소를 만들고 모든 것을 넣습니다. 심포니와 독립적으로 개발하고 싶습니다. symfony-project deps에서 symfony.com/doc/current/cookbook/workflow/…에 언급 된 대로 파일을 업데이트 한 다음 symfony 관련 항목에 대해 하나 이상의 응용 프로그램 번들 ( php app/console generate:bundle)을 작성하십시오.
KingCrunch

11

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

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

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

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와 같은)은 번들을 통해서만 발견되기 때문에 번역 파일을 사용하고 싶을 때 문제가 발생합니다 .

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


5

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

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


그러나 응용 프로그램을 KnpRadBundle에 연결합니다 ...이 문제에 대한 쉬운 접근 방법이 없습니까?
Daniel Ribeiro

1
symfony에 의존하는 부분 (컨트롤러, 모델, 템플릿 등)은 symfony를 사용하므로 확장 클래스, 헬퍼 등을 사용하므로 항상 symfony에 연결됩니다. 단독으로 작동하는 클래스는 회사 네임 스페이스에 있으며 종속성 컨테이너를 사용하여로드 할 수 있습니다. 이러한 클래스는 프레임 워크 독립적 일 수 있습니다.
miguel_ibero

1
문제는 Bundle공개적으로 공유 한다는 개념입니다 . 일부 응용 프로그램을 작성할 때 의도적으로 커뮤니티 중심 모듈로 구축 한 부분을 제외하고 코드를 공유하고 싶지 않습니다. 내가 잘못?
Daniel Ribeiro

번들을 공유 할 필요는 없습니다. 번들은 일부 구성이있는 클래스 그룹으로 생각하십시오. 각 프로젝트마다 다른 번들을 가질 수 있습니다.
miguel_ibero


5

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

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

TLDR :

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

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

TLDR :

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


-2

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

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

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

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.