201703 ec-cube 3.1開発方針説明会:機能カスタマイズ編...

28
EC-CUBE 3.1 開開開開開開 開開開開開開開開開 開開開開開開開開開開開開開開開開開開開

Upload: ec-cube

Post on 22-Mar-2017

143 views

Category:

Software


0 download

TRANSCRIPT

Page 1: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

EC-CUBE 3.1 開発方針説明

機能カスタマイズ編機能カスタマイズのためのアーキテクチャ

Page 2: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

概要

#1985 で、 v3.1 に向けた実験的な実装を行っています。詳細は主にShoppingController のソースコメントに記載しています。

• forward(Sub Request) を使用して、 Controller の処理を抽象化。継承を使用せず、処理をオーバーライドできるようにした。

• Order 関連の FormType の抽象化• 単価集計を CalculateService にまとめて、 Strategy パターンを適用• 支払を PaymentService にまとめて、 Adapter パターンを適用

Page 3: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

概要

その他、以下アーキテクチャの変更をしています。

• Symfony3.2• v3.1 では Symfony3.4 LTS を採用予定

• Silex2.0• Pimple3.0• Doctrine2.5• SensioFrameworkExtraBundle• Inheritance Mapping

Page 4: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

カスタマイズ方法の改善

• アノテーションの採用• forward(Sub Request) の使用• Inheritance Mapping の採用• 単価集計や支払いなどの処理にデザインパターンを適用• プラグインを使用しないカスタマイズ

Page 5: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

アノテーションの採用

• 新たに、 Doctrine アノテーション、 SensioFrameworkExtraBundle アノテーションが使用できるようになりました。•Entity の定義や、 コントローラのルーティング設定をアノテーションで記述できるようになり、より簡易に拡張が可能になりました。

※ 現在、既存のエンティティや、ルーティングは従来の Yaml や PHP での定義となっていますが、将来的にはすべてアノテーションに置き換えられる予定です。

Page 6: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

サポートされているアノテーション

•Doctrine アノテーション•SensioFrameworkExtraBundle アノテーション

Page 7: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

サポートされているアノテーション

Doctrine アノテーションhttp://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

@Column, @Entity, @GeneratedValue, @Index, @Id, @InheritanceType, @JoinColumn, @JoinColumns, @JoinTable, @ManyToOne, @ManyToMany, @MappedSuperclass, @OneToOne, @OneToMany, @OrderBy, @SequenceGenerator, @Table, @UniqueConstraint...

Page 8: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

サポートされているアノテーション

SensioFrameworkExtraBundle アノテーションhttp://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html

• @Route 及び @Method• @Template• @Security

Page 9: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

forward(Sub Request) の使用

従来、本体に手を入れずに、コントローラの処理を拡張する場合は、主に以下のような方法がありました。

• 継承して別のインスタンスへ置き変える• イベントハンドラで頑張る

これらの方法は、プラグインとコントローラの結合度が強くなり、開発効率を下げる要因となっていました。

今回、カテゴリなどのブロックの処理に使用している Sub Request を流用し、コントローラ内の処理を簡便に、他のコントローラへ移譲できるようになりました。

Page 10: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

forward(Sub Request) の使用

ApplicationTrait::forward($path, $requestParameters) というメソッドが追 加されており、 $path で指定したコントローラへ処理を移譲することが

できます。このメソッドは、 Response を返します。

コントローラ内で、この Response を return すると、レスポンスが出力されます。return しなければ、内部の処理のみ実行されます。

コントローラのメソッドは、ルーティングを介して、緩く結合しているイメージです。

Page 11: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

forward(Sub Request) の使用

例として、 ShoppingController::index() メソッドは以下のような実装になっています。

Page 12: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

src/Eccube/Controller/ShoppingController.php

/** * 購入画面表示

* * @Route("/", name="shopping") * @Template("Shopping/index.twig") */ 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"));

Page 13: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

src/Eccube/Controller/ShoppingController.php

// 受注のマイナスチェック

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

Page 14: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

forward(Sub Request) の使用

例えば、カートチェックの振舞いを変更したい場合は、shopping/checkToCart のルーティングをオーバーライドしたメソッドを作

成します。この処理は、 app/Acme/Controller 以下や、プラグインなどで拡張できます。

Page 15: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

app/Acme/Controller/ExampleController.php

/** * @Route("/shopping") */class ExampleController{ /** * カート画面のチェック

* * @Route("/checkToCart", name="shopping/checkToCart") */ public function checkToCart(Application $app, Request $request) { $cartService = $app['eccube.service.cart'];

// カートチェック

if (!$cartService->isLocked()) { log_info(' カートが存在しません '); // カートが存在しない、カートがロックされていない時はエラー

return $app->redirect($app->url('cart')); }

Page 16: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

app/Acme/Controller/ExampleController.php

// 独自の処理を記述

log_info(' カートの内容をチェックしました ');

// 各コントローラ間の値の受け渡しには $app['request_scope'] を使用可能

$Order = $app['request_scope']->get('Order'); if ($Order) { $Order->setNote(' 独自カスタマイズ処理を通過しました '); $app['orm.em']->flush($Order); }

return new Response(); }}

Page 17: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

forward(Sub Request) の使用

forwardChain を使用することで、複数の forward を連続してつなげることも可能です。forward を活用することにより、各ルーティングの処理をコンパクトにまとめることができます。依存するクラスも少ないため、簡単にテストを記述することが可能です。

Page 18: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

ExampleTest

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').' へリダイレクト '); }

Page 19: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

ExampleTest

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(), ' 空のレスポンスを返却 '); }

Page 20: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

Inheritance Mapping の採用

Inheritance Mappinghttp://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html

データベースのテーブルに新たなカラムを追加したい場合に Inheritance Mapping を使用できるようになりました。

例えば、 商品 (Product) に `ExampleField` という項目を追加したい場合 は、以下のようなクラスを作成し、 schema-tool で UPDATE するだけで

す!※http://ec-cube.github.io/collaboration_migration#db-1

Page 21: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

Acme/Entity/ExamplePayment.php

/** * Product の拡張

* @Entity * @Table(name="example_product") */class ExamplePayment extends \Eccube\Entity\Product{ /** * @Column(name="example_field", type="string") */ public $ExampleField;}

Page 22: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

単価集計や、支払いなどの処理にデザインパターンを適用

一部のビジネスロジックにデザインパターンを適用し、柔軟かつ効率的にカスタマイズできるようになりました。

以下、 ShoppingController の一部です。 プラグイン側では、 CalculateStrategy や PaymentMethod クラスを実装

することで、独自の決済手段を実装可能です。

現在は、商品購入処理のみとなっていますが、商品管理など他の機能にも適用していく予定です。

Page 23: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

src/Eccube/Controller/ShoppingController.php

// 購入処理

// 集計は , この 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')); }

Page 24: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

プラグインを使用しないカスタマイズ

新たに `app/Acme` 以下に、カスタマイズ用のプログラムを置けるようになりました。

• プラグインにするまでもないような、ちょっとしたカスタマイズ• 既存のプラグインの振舞いを変更したい場合• プラグインでは対応しにくい大規模カスタマイズ

などに利用できます。

※`Acme` という namespace は、任意のものに変更可能です。

Page 25: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

参考実装

• プラグインの参考実装• プラグインを使用しないカスタマイズの参考実装

Page 26: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

プラグインの参考実装

https://github.com/nanasess/ec-cube/tree/CalculateStrategy/app/Plugin/ExamplePlugin

• Plugin\ExamplePlugin\Controller\ExampleController - ShoppingController をオーバーライドし、独自の決済ボタンを実装しています。

• Plugin\ExamplePlugin\Payment\Method\ExamplePaymentCreditCard - 独自の決済処理を実装しています。

• Plugin\ExamplePlugin\Entity\ExamplePayment - dtb_payment に独自のカラムを追加しています。

Page 27: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

プラグインを使用しないカスタマイズの参考実装

https://github.com/nanasess/ec-cube/tree/CalculateStrategy/app/Acme

• Acme\Controller\TestController - 独自コントローラの作成例です。• Acme\Controller\AController - 上記 TestController の拡張例です。• Acme\Controller\RoutingTestController - 管理画面 , user_data の拡張

例です。• Acme\Entity\ExtendedProduct - エンティティの拡張例です。 public

プロパティを使用しています。

Page 28: 201703 EC-CUBE 3.1開発方針説明会:機能カスタマイズ編 02_機能カスタマイズのためのアーキテクチャ

thanks.