1. ホーム
  2. symfony

[解決済み】Symfony 2.xでは、本当にすべてをバンドルにすべきなのか?

2022-04-12 10:35:37

質問

のような質問を意識しています。 これ ここでは、バンドルという Symfony 2 の一般的な概念について議論されることが多いようです。

つまり、特定のアプリケーション、たとえば twitter のようなアプリケーションでは、本当にすべてを一般的なバンドルの中に入れるべきなのか、ということです。 公式ドキュメント と言うのでしょうか?

この質問をした理由は、一般的にアプリケーションを開発するとき、フルスタックのグルーフレームワークにコードを高度に結合させたくないからです。

もし私がSymfony 2ベースのアプリケーションを開発し、ある時点でSymfony 2は本当に最良の選択ではないと判断した場合、次のようになります。 開発を継続する それは問題ないのでしょうか?

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

EDIT#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+ のテンプレート推測ツールをオーバーライドする

を作成しました。 バンドル がそれをやってくれる。

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 フォルダーに格納されます。例えば、上の例のコントローラにある2つのアクションのテンプレートは、以下の場所にあります。

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

テンプレートを参照する場合は、bundleの部分を省略すればよい。

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