まず、 前の パートを読んでくださった皆さんに感謝します。 私は多くの興味深いコメントを書きましたが、実際、なぜこれをすべて書いているのか説明する必要があることに気付きました。 フレームワークからコントローラーを分離する必要があるのはなぜですか? ほとんどの場合、それについて考える必要はありません。
コントローラを別のフレームワークに転送する必要がある可能性はほぼゼロです。 (ラファエル・ドムス)
よく言って、それに同意します。 フレームワークを変更する人は通常いないため、これを行う必要はありません。 誰かがあなたのコードを別のフレームワーク上のアプリケーションで使いたいと思う可能性はありますが。 しかし:
すべてがサービス層に依存する「シン」コントローラーがある場合、別のフレームワーク(Rafael Doms)に書き換えることが基本になります。
実際、コントローラーにはコードはほとんどなく、サービスへの呼び出し、それらからのデータの受信、および応答の返送のみが必要です。 それらは別のフレームワークに書き換えることさえできません。 また、コードを配布しない場合は、コントローラーをフレームワークから分離しても意味がありません。
しかし、これを行うと、少し幸せになります。 コントローラーがより独立するだけでなく、開発者としても独立します。 ヘルパー関数、注釈、または魔法に頼る必要はもうありません。自分ですべてを行うことができます。 私の印象を正確に説明しているので、次の引用が本当に好きです。
新しいコントローラーを書くことは以前よりも便利になりました。そのため、コードの本質についてもっと考えます(Kevin Bond)
それが、私がこの一連の投稿を書いている理由です。 開発者は、フレームワーク内で実際に何が行われているのか、そしてそれがどのように役立つのかを開発者に理解させることができます。 開発者は、より自信を持って、より独立しています。 彼らは「シンフォニー開発者」ではなくなり、一般的な意味でより熟練した開発者になります。 この一連の記事は、依存関係を識別するための優れた演習です。 ちなみに、コントローラーでの合意も依存関係です。たとえ目から隠されていても、「接続されたレーダー」を訓練する機会があります。
小枝テンプレート
フレームワークに依存しないコントローラーの最後のいくつかのステップを見てみましょう。 前のパートでは、すべての注釈を削除し、代わりに構成ファイルを使用しました。 また、データベースからデータを取得してテンプレートをレンダリングできるようにするいくつかの依存関係も導入しました。 ただし、テンプレート名にはまだ名前空間としてバンドルの名前が含まれています。
class ClientController { ... public function detailsAction(Client $client) { return new Response( $this->templating->render( '@MatthiasClientBundle/Resources/views/Client/Details.html.twig', ... ) ); } }
バンドルがないアプリケーションでこのコントローラーを機能させることにしたので、より一般的な名前、たとえば
MatthiasClient
を選択する必要があります。 次に、この名前をTwigテンプレートの名前空間として登録する必要があります。 これを行うには、
Twig_Loader_Filesystem::addPath('///', '')
呼び出します。 良いことは、Symfony 2アプリケーションでは、これを
twig.paths
構成
twig.paths
指定することで実行できることです:
# config.yml: twig: paths: "%kernel.root_dir%/../src/Matthias/Client/View": "MatthiasClient"
このパスを
config.yml
追加すると、コントローラーのコードは次のように変更できます。
return new Response( $this->templating->render( '@MatthiasClient/Client/Details.html.twig', ... ) );
さらに良い:構成の事前配線
MatthiasClientBundle
を初めてプロジェクトに接続するときに構成を編集する必要があるため、あまりエレガントなソリューションは得られませんでした。 より良いオプションがあります。バンドルの拡張クラスから設定にプログラムで値を追加できます。 拡張機能が
PrependExtensionInterface
を実装し、
PrependExtensionInterface
のメイン値の前に追加する必要がある値の配列を提供する必要があります。
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; class MatthiasClientExtension extends Extension implements PrependExtensionInterface { ... public function prepend(ContainerBuilder $container) { $container->prependExtensionConfig( 'twig', array( 'paths' => array( '%kernel.root_dir%/../src/Matthias/Client/View' => 'MatthiasClient' ) ) ); } }
この値は自動的に追加されるため、
config.yml
から余分な行を削除できます。
HttpFoundation依存関係を取り除く
前回の記事では、一部の読者に疑問を投げかけました。
そして今、コントローラーは明らかにDoctrineとHttpFoundationに依存しています! 注釈付きのバージョンでは、そうではありませんでした! (ジェリーヴァンダーマイセン)
私の意見では、Doctrineに依存することはそれほど悪くありません。 これは私が気に入ったライブラリです(Twigなど)。 ORM / ODMからの切り離しも興味深いものであり、実装することもできますが、それでもこの一連の投稿の範囲外です。
しかし、Jerryは正しいです。注釈を削除し、HttpFoundationコンポーネントから
Request
クラスと
Response
クラスへの依存関係を追加します。 しかし、彼とは異なり、Symfony以外のフレームワークがHTTPの抽象化レイヤーとしてHttpFoundationをサポートしているため、これはフレームワークから切り離すための良いステップだと思います。
ただし、HttpFoundationに応じてもう1つの手順を実行して停止できます。
Request
および
Response
オブジェクトを取り除く必要があります。 次のようにコントローラーを変更します。
public function detailsAction($id) { $client = $this->clientRepository->find($id); if (!($client instanceof Client)) { return array(null, 404); } return array( $this->templating->render( '@MatthiasClient/Client/Details.html.twig', array('client' => $client) ), 200 ); }
HttpFoundationのクラスはここでは言及されていません! これで、コントローラーをこのコンポーネントにバインドするラッパーコントローラーを追加できます。
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class SymfonyClientController { private $clientController; public function __construct(ClientController $clientController) { $this->clientController = $clientController; } public function detailsAction(Request $request) { $result = $this->clientController->detailsAction($request->attributes->get('id')); list($content, $status) = $result; if ($status === 404) { throw new NotFoundHttpException($content); } return new Response($content, $status); } }
はい、コードはもう少し理解しにくくなっています。 ですから、そうすることはお勧めしません。 それでも、これが本当に可能であることをお見せしたいと思います。
「アクション」メソッドを取り除く
ディスカッションの最後のトピック:コントローラーアクション。 事実上の標準は、関連するアクションが1つのコントローラークラスにパッケージ化されることです。 たとえば、すべてのクライアントアクションは
ClientController
クラスに含まれている必要があります。 つまり、
newAction
、
editAction
などのメソッドがあります。 そして、コンストラクターで依存性注入を使用すると、アクションゲームで使用されないものもあります。
この問題の解決策は実際には非常に単純であり、コントローラーコードの認識を大幅に簡素化します。 また、コントローラーの検索が容易になっています。 アクションを少し異なる方法でグループ化する必要があるだけです。 概して、各アクションは個別のクラスに割り当てられ、そのようなアクションコントローラーが表示されるディレクトリはリンクになります。例は次のとおりです
Controller\Client\New
、
Controller\Client\Edit
など。 これらの各クラスには、コントローラーの実行時に呼び出される1つのパブリックメソッドがあります。 それを
__invoke
と呼びましょう:
namespace Matthias\Client\Controller\Client; class Details { public function __construct(...) { // } public function __invoke(Client $client) { ... }
この手法について最初に学んだのは、Paul Jonesの著書「PHPでのレガシーアプリケーションの近代化」です。 私はすでにそれを数回使用しました、そして、場合によってはコントローラーでの作業がはるかに快適になったと言わなければなりません!
おわりに
さて、次に別のコントローラーを作成するときに、いくつかのアイデアを提供しました。 また、フレームワーク自体をよく知っていることを願っています。 そして、私はあなたの心が自由であることを願っています:)