web-dev-qa-db-ja.com

本当にすべてがSymfony 2.xのバンドルである必要がありますか?

私は this のような質問を知っています。人々は一般的なSymfony 2バンドルの概念について議論する傾向があります。

問題は、たとえばTwitterのようなアプリケーションのような特定のアプリケーションでは、すべてが本当に official docs say?

私がこれを求めている理由は、一般にアプリケーションを開発するとき、コードをフルスタックの接着剤フレームワークに高度に結合したくないためです。

Symfony 2ベースのアプリケーションを開発し、ある時点で、Symfony 2が開発を続ける最良の選択肢ではないと判断した場合、それは私にとって問題になりますか?

それで、一般的な質問は、なぜすべてがバンドルであることが良いことなのかということです。

EDIT#1

この質問をしてからほぼ1年が経ち、このトピックに関する知識を共有するために 記事 を書きました。

202
Daniel Ribeiro

このトピックについて、より徹底的で更新されたブログ記事を書きました: http://elnur.pro/symfony-without-bundles/


いいえ、すべてがバンドルになっている必要はありません。次のような構造を持つことができます。

  • src/Vendor/Model —モデルの場合、
  • src/Vendor/Controller —コントローラーの場合、
  • src/Vendor/Service —サービスの場合、
  • src/Vendor/Bundlesrc/Vendor/Bundle/AppBundleなどのバンドルの場合、
  • 等.

この方法では、AppBundleに実際にSymfony2固有のものだけを入れます。後で別のフレームワークに切り替える場合は、Bundle名前空間を削除し、選択したフレームワークのものに置き換えます。

ここで提案しているのは、app特定のコード用です。再利用可能なバンドルについては、 ベストプラクティスを使用することをお勧めします

エンティティをバンドルに入れない

エンティティをsrc/Vendor/Modelのバンドル外に保持するために、config.ymldoctrineセクションを変更しました

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:UserなどのModelで始まります。

サブ名前空間を使用して、src/Vendor/User/Group.phpなどの関連するエンティティをグループ化できます。この場合、エンティティの名前はModel:User\Groupです。

コントローラーをバンドルに入れない

まず、 JMSDiExtraBundleconfig.ymlに追加して、サービスのsrcフォルダーをスキャンするよう指示する必要があります。

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;
        }
    }
}

そして、これをconfig.ymlに追加して使用するようSymfonyに指示します。

parameters:
    jms_di_extra.template_listener.class: Vendor\Listener\TemplateListener

バンドルなしでテンプレートを使用する

これで、バンドルからテンプレートを使用できます。 app/Resources/viewsフォルダーの下に保管してください。たとえば、上記の例のコントローラーのこれら2つのアクションのテンプレートは、次の場所にあります。

  • app/Resources/views/User/add.html.twig
  • app/Resources/views/User/profile.html.twig

テンプレートを参照するときは、バンドル部分を省略します。

{% include ':Controller:view.html.twig' %}
217

もちろん、アプリケーションを分離できます。ライブラリとして開発し、symfony vendor/- folderに統合するだけです(Symfony2.0またはSymfony2.1を使用するかどうかに応じて、depsまたはcomposer.jsonを使用します)。ただし、Symfony2がコントローラー(など)を見つけるライブラリの「フロントエンド」として機能するバンドルが少なくとも1つ必要です。

20
KingCrunch

通常のsymfonyディストリビューションは、フルスタックフレームワークから使用する機能の量に応じて、余分な(アプリケーション)バンドルなしで機能します。

たとえば、コントローラーは、自動ロードされるとすぐにプロジェクト構造内のどこにでも配置できる呼び出し可能オブジェクトにすることができます。

ルーティング定義ファイルでは、次を使用できます。

test:
    pattern:   /test
    defaults:  { _controller: Controller\Test::test }

それは、Symfony\Component\HttpFoundation\Responseオブジェクトを返さなければならないという事実によってのみフレームワークに結び付けられた、どんな単純な古いphpオブジェクトでもかまいません。

twigテンプレート(またはその他)はapp/Resources/views/template.html.twigのように配置でき、::template.html.twig論理名を使用してレンダリングできます。

すべてのDIサービスはapp/config/config.ymlで定義できます(またはapp/config/services.ymlからインポートできます。また、すべてのサービスクラスは、フレームワークにまったく関連付けられていないプレーンな古いphpオブジェクトでもかまいません。

これらはすべて、デフォルトでsymfonyのフルスタックフレームワークによって提供されます。

問題が発生するのは、バンドルonlyを通じて検出されるため、翻訳ファイル(xliffなど)を使用する場合です。

symfony-light ディストリビューションは、通常バンドルを通じてのみ発見されるすべてを発見することにより、この種の問題を解決することを目的としています。

11
Florian

すでに5年が経過しているので、Symfonyバンドルに関する記事はもう少しあります。

  1. Symfonyのバンドルとは? by Iltar van der Berg。

TLDR:

アプリケーションに直接複数のバンドルが必要ですか?ほとんどありません。依存関係のスパゲッティを防ぐために、AppBundleを書くほうがよいでしょう。 ベストプラクティス に従うだけで問題なく動作します。

  1. Symfony:バンドル方法 by Toni Uebernickel。

TLDR:

アプリケーションロジック用にAppBundleというバンドルを1つだけ作成します。 1つのAppBundle-ただし、そこにアプリケーションロジックを入れないでください!

5
Reshat Belyalov

KnpRadBundle を使用できます。これは、プロジェクト構造を単純化しようとします。

別のアプローチは、たとえばバンドルにsrc/Company/Bundle/FrontendBundleを使用し、symfonyに依存せず、フレームワークの外部で再利用できるクラスにsrc/Company/Stuff/Class.phpを使用することです

5
miguel_ibero