We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v3.n への課題として、 #1985 の内容を issues としておきます。
experimental/3.1 ブランチ は、 v3.1 向けた実験的な実装です。詳細は ShoppingController のソースコメントに記載
その他、以下アーキテクチャの変更をしています。
新たに、 Doctrine アノテーション、 SensioFrameworkExtraBundle アノテーションが使用できるようになりました。 Entity の定義や、 コントローラのルーティング設定をアノテーションで記述できるようになり、より簡易に拡張が可能になりました。
従来、本体に手を入れずに、コントローラの処理を拡張する場合は、主に以下のような方法がありました。
これらの方法は、プラグインとコントローラの結合度が強くなり、開発効率を下げる要因となっていました。
今回、カテゴリなどのブロックの処理に使用している Sub Request を流用し、コントローラ内の処理を簡便に、他のコントローラへ移譲できるようになりました。
ApplicationTrait::forward($path, $requestParameters) というメソッドが追加されており、 $path で指定したコントローラへ処理を移譲することができます。 このメソッドは、Response を返します。 コントローラ内で、この Response を return すると、レスポンスが出力されます。 return しなければ、内部の処理のみ実行されます。
ApplicationTrait::forward($path, $requestParameters)
$path
Response
return
コントローラのメソッドは、ルーティングを介して、緩く結合しているイメージです。
例として、 ShoppingController::index() メソッドは以下のような実装になっています。
ShoppingController::index()
/** * 購入画面表示 * * @Route("/", name="shopping") * @Template("Shopping/index.twig") * * @param Application $app * @param Request $request * @return array */ public function index(Application $app, Request $request) { // カートチェック $response = $app->forward($app->path("shopping/checkToCart")); if ($response->isRedirection() || $response->getContent()) { return $response; } // 受注情報を初期化 $response = $app->forward($app->path("shopping/initializeOrder")); if ($response->isRedirection() || $response->getContent()) { return $response; } // 単価集計し, フォームを生成する $app->forwardChain($app->path("shopping/calculateOrder")) ->forwardChain($app->path("shopping/createForm")); // 受注のマイナスチェック $response = $app->forward($app->path("shopping/checkToMinusPrice")); if ($response->isRedirection() || $response->getContent()) { return $response; } // 複数配送の場合、エラーメッセージを一度だけ表示 $app->forward($app->path("shopping/handleMultipleErrors")); $Order = $app['request_scope']->get('Order'); $form = $app['request_scope']->get(OrderType::class); return [ 'form' => $form->createView(), 'Order' => $Order ]; }
例えば、カートチェックの振舞いを変更したい場合は、 shopping/checkToCart のルーティングをオーバーライドしたメソッドを作成します。 この処理は、 app/Acme/Controller 以下や、プラグインなどで拡張できます。
shopping/checkToCart
app/Acme/Controller
/** * @Route("/shopping") */ class ExampleController { /** * カート画面のチェック * * @Route("/checkToCart", name="shopping/checkToCart") * @param Application $app * @param Request $request * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function checkToCart(Application $app, Request $request) { $cartService = $app['eccube.service.cart']; // カートチェック if (!$cartService->isLocked()) { log_info('カートが存在しません'); // カートが存在しない、カートがロックされていない時はエラー return $app->redirect($app->url('cart')); } // 独自の処理を記述 log_info('カートの内容をチェックしました'); // 各コントローラ間の値の受け渡しには $app['request_scope'] を使用可能 $Order = $app['request_scope']->get('Order'); if ($Order) { $Order->setNote('独自カスタマイズ処理を通過しました'); $app['orm.em']->flush($Order); } return new Response(); } }
forwardChain を使用することで、複数の forward を連続してつなげることも可能です。
forward を活用することにより、各ルーティングの処理をコンパクトにまとめることができます。 依存するクラスも少ないため、簡単にテストを記述することが可能です。
public function testCheckToCart() { $Controller = new \Eccube\Controller\ShoppingController(); $this->assertInstanceOf('\Eccube\Controller\ShoppingController', $Controller); $Request = Request::create($this->app->path('shopping/checkToCart'), 'GET'); $Response = $Controller->checkToCart($this->app, $Request); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $Response); $this->assertTrue($Response->isRedirect($this->app->url('cart')), $this->app->url('cart').'へリダイレクト'); } public function testCheckToCartIn() { $Controller = new \Eccube\Controller\ShoppingController(); // カートに商品を投入 $cartService = $this->app['eccube.service.cart']; $cartService->addProduct(1); $cartService->lock(); $this->assertInstanceOf('\Eccube\Controller\ShoppingController', $Controller); $Request = Request::create($this->app->path('shopping/checkToCart'), 'GET'); $Response = $Controller->checkToCart($this->app, $Request); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $Response); $this->assertEmpty($Response->getContent(), '空のレスポンスを返却'); }
データベースのテーブルに新たなカラムを追加したい場合に Inheritance Mapping を使用できるようになりました。 例えば、 商品(Product)に ExampleField という項目を追加したい場合は、以下のようなクラスを作成し、 schema-tool で UPDATE するだけです!
ExampleField
/** * Product の拡張 * @Entity * @Table(name="example_product") */ class ExamplePayment extends \Eccube\Entity\Product { /** * @Column(name="example_field", type="string") */ public $ExampleField; }
従来は、 OneToOne や OneToMany のリレーションを作成し、 JOIN で頑張るしかありませんでした。
一部のビジネスロジックにデザインパターンを適用し、柔軟かつ効率的にカスタマイズできるようになりました。 以下、 ShoppingController の一部です。 プラグイン側では、 CalculateStrategy や PaymentMethod クラスを実装することで、独自の決済手段を実装可能です。
// 購入処理 // 集計は,この1行で実行可能 // プラグインで CalculateStrategy をセットしたりしてアルゴリズムの変更が可能 // 集計はステートレスな実装とし、再計算時に状態を考慮しなくても良いようにする $app['eccube.service.calculate']($Order, $Order->getCustomer())->calculate(); // 支払処理 $paymentService = $app['eccube.service.payment']($Order->getPayment()->getServiceClass()); // PaymentMethod クラスは、 Cash(銀行振込)、 CreditCard(クレジットカード)などを取得する $paymentMethod = $app['payment.method.request']($Order->getPayment()->getMethodClass(), $form, $request); // PaymentMethod 内の処理で、必要に応じて別のコントローラへ forward(移譲)可能 $dispatcher = $paymentService->dispatch($paymentMethod); // 決済処理中. if ($dispatcher instanceof Response && ($dispatcher->isRedirection() || $dispatcher->getContent())) { // $paymentMethod->apply() が Response を返した場合は画面遷移 return $dispatcher; } $PaymentResult = $paymentService->doCheckout($paymentMethod); // 決済実行 if (!$PaymentResult->isSuccess()) { $em->getConnection()->rollback(); return $app->redirect($app->url('shopping_error')); } $em->flush(); $em->getConnection()->commit(); log_info('購入処理完了', array($Order->getId()));
現在は、商品購入処理のみとなっていますが、商品管理など他の機能にも適用していく予定です。
新たに app/Acme 以下に、カスタマイズ用のプログラムを置けるようになりました。
app/Acme
などに利用できます。
Acme という namespace は、任意のものに変更可能です。
Acme
Plugin\ExamplePlugin\Controller\ExampleController
Plugin\ExamplePlugin\Payment\Method\ExamplePaymentCreditCard
Plugin\ExamplePlugin\Entity\ExamplePayment
dtb_payment
本プラグインは、以下のコマンドでインストール可能です。
php app/console plugin:develop --code ExamplePlugin install php app/console plugin:develop --code ExamplePlugin enable
Acme\Controller\TestController
Acme\Controller\AController
Acme\Controller\RoutingTestController
Acme\Entity\ExtendedProduct
@ParamConverter
The text was updated successfully, but these errors were encountered:
No branches or pull requests
v3.n への課題として、 #1985 の内容を issues としておきます。
experimental/3.1 ブランチ は、 v3.1 向けた実験的な実装です。詳細は ShoppingController のソースコメントに記載
その他、以下アーキテクチャの変更をしています。
カスタマイズ方法の改善
アノテーションの採用
新たに、 Doctrine アノテーション、 SensioFrameworkExtraBundle アノテーションが使用できるようになりました。
Entity の定義や、 コントローラのルーティング設定をアノテーションで記述できるようになり、より簡易に拡張が可能になりました。
サポートされているアノテーション
Doctrine アノテーション
SensioFrameworkExtraBundle アノテーション
forward(Sub Request) の使用
従来、本体に手を入れずに、コントローラの処理を拡張する場合は、主に以下のような方法がありました。
これらの方法は、プラグインとコントローラの結合度が強くなり、開発効率を下げる要因となっていました。
今回、カテゴリなどのブロックの処理に使用している Sub Request を流用し、コントローラ内の処理を簡便に、他のコントローラへ移譲できるようになりました。
ApplicationTrait::forward($path, $requestParameters)
というメソッドが追加されており、$path
で指定したコントローラへ処理を移譲することができます。このメソッドは、
Response
を返します。コントローラ内で、この
Response
をreturn
すると、レスポンスが出力されます。return
しなければ、内部の処理のみ実行されます。コントローラのメソッドは、ルーティングを介して、緩く結合しているイメージです。
例として、
ShoppingController::index()
メソッドは以下のような実装になっています。例えば、カートチェックの振舞いを変更したい場合は、
shopping/checkToCart
のルーティングをオーバーライドしたメソッドを作成します。この処理は、
app/Acme/Controller
以下や、プラグインなどで拡張できます。forwardChain を使用することで、複数の forward を連続してつなげることも可能です。
forward を活用することにより、各ルーティングの処理をコンパクトにまとめることができます。
依存するクラスも少ないため、簡単にテストを記述することが可能です。
Inheritance Mapping の採用
データベースのテーブルに新たなカラムを追加したい場合に Inheritance Mapping を使用できるようになりました。
例えば、 商品(Product)に
ExampleField
という項目を追加したい場合は、以下のようなクラスを作成し、 schema-tool で UPDATE するだけです!従来は、 OneToOne や OneToMany のリレーションを作成し、 JOIN で頑張るしかありませんでした。
単価集計や、支払いなどの処理にデザインパターンを適用
一部のビジネスロジックにデザインパターンを適用し、柔軟かつ効率的にカスタマイズできるようになりました。
以下、 ShoppingController の一部です。
プラグイン側では、 CalculateStrategy や PaymentMethod クラスを実装することで、独自の決済手段を実装可能です。
現在は、商品購入処理のみとなっていますが、商品管理など他の機能にも適用していく予定です。
プラグインを使用しないカスタマイズ
新たに
app/Acme
以下に、カスタマイズ用のプログラムを置けるようになりました。などに利用できます。
Acme
という namespace は、任意のものに変更可能です。参考実装
プラグインの参考実装
Plugin\ExamplePlugin\Controller\ExampleController
- ShoppingController をオーバーライドし、独自の決済ボタンを実装しています。Plugin\ExamplePlugin\Payment\Method\ExamplePaymentCreditCard
- 独自の決済処理を実装しています。Plugin\ExamplePlugin\Entity\ExamplePayment
-dtb_payment
に独自のカラムを追加しています。本プラグインは、以下のコマンドでインストール可能です。
プラグインを使用しないカスタマイズの参考実装
Acme\Controller\TestController
- 独自コントローラの作成例です。Acme\Controller\AController
- 上記 TestController の拡張例です。Acme\Controller\RoutingTestController
- 管理画面, user_data の拡張例です。Acme\Entity\ExtendedProduct
- の拡張例です。 public プロパティを使用しています。その他
@ParamConverter
アノテーションも使いたいThe text was updated successfully, but these errors were encountered: