Slide 1

Slide 1 text

実 践 編 PHPメンターズ 後藤 秀宣 @hidenorigoto 1

Slide 2

Slide 2 text

自己紹介 • 後藤 秀宣(ごとう ひでのり) • @hidenorigoto • インクス株式会社/PHPメンターズ (KnpLabs Japanメンバー) • PHPは10年くらい利用 • 日本Symfonyユーザー会 • symfony 1.4の本 2

Slide 3

Slide 3 text

アジェンダ • 実践編の流れ • 構築するアプリの説明 • Symfony2基礎知識 • 構築 • Symfony2インストールと設定の概略、ファイルとディレクトリ、バンドル、ジェネレーター(エン ティティクラスとリポジトリクラス)、エンティティ調整と初期データの投入、ページフローの実 装(ルーティングとコントローラ、テンプレート)、フォームの基本、TwigとSymfonyバインディ ング、フォームとバリデーション、CSRFトークン、フォームの詳細、Doctrine2 ORM基礎、バリ デーション定義、TwitterBootstrapの組み込み、ファンクショナル(受入)テスト、ユニットテス ト 3

Slide 4

Slide 4 text

実践編の流れ • 基礎解説 +ライブコーディング +実況解説 • 上記を7イテレーション 質問はイテレーションの区切りor随時 • トイレも随時 4

Slide 5

Slide 5 text

構築するアプリ • アンケート(超簡易版) 5

Slide 6

Slide 6 text

要件の整理 • 1回分のアンケート実施のみ • アンケート項目は3タイプ固定 • 選択式1,選択式2、自由記入 • アンケート項目数可変 • アンケート設問はDBに入れる • 回答情報もDBに入れる 6

Slide 7

Slide 7 text

データモデル 7 ઃ ܭ ࡁ 設問項目 回答 回答詳細

Slide 8

Slide 8 text

データベース 8 mysql> show tables; +-------------------------+ | Tables_in_questionnaire | +-------------------------+ | answer | | answer_detail | | questionnaire_item | +-------------------------+ 3 rows in set (0.01 sec)

Slide 9

Slide 9 text

answerテーブル 9 CREATE TABLE `answer` ( `id` int(11) NOT NULL AUTO_INCREMENT, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

Slide 10

Slide 10 text

answer_detailテーブル 10 CREATE TABLE `answer_detail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `answer_id` int(11) DEFAULT NULL, `questionnaire_item_id` int(11) DEFAULT NULL, `input` longtext, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `questionnaire_item_answer_idx` (`answer_id`,`questionnaire_item_id`), KEY `IDX_DCFF4534AA334807` (`answer_id`), KEY `IDX_DCFF45344DF37C59` (`questionnaire_item_id`), CONSTRAINT `FK_DCFF45344DF37C59` FOREIGN KEY (`questionnaire_item_id`) REFERENCES `questionnaire_item` (`id`), CONSTRAINT `FK_DCFF4534AA334807` FOREIGN KEY (`answer_id`) REFERENCES `answer` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

Slide 11

Slide 11 text

questionnaire_itemテーブル 11 CREATE TABLE `questionnaire_item` ( `id` int(11) NOT NULL AUTO_INCREMENT, `text` longtext NOT NULL, `answer_type` varchar(255) NOT NULL, `optionality` varchar(255) NOT NULL, `item_number` int(11) NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `questionnaireitem_itemnumber_idx` (`item_number`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

Slide 12

Slide 12 text

初期データ • 設問項目データが投入されている (questionnaire_item) 12

Slide 13

Slide 13 text

回答データ • 回答データが登録されたところ 13

Slide 14

Slide 14 text

リポジトリ • https://github.com/ phpmentors-jp/kphpug- questionnaire • コミットログとタグで、流れを把握しや すくしてあります。 14

Slide 15

Slide 15 text

Symfony2の基礎知識 15

Slide 16

Slide 16 text

アーキテクチャ 16 • サービスコンテナ • Dependency Injectionコンテナ • カーネルとバンドル

Slide 17

Slide 17 text

サービスコンテナ • オブジェクトの構成に関する知識が集約 された場所 • DependencyInjectionコンポーネントとし て単体で利用可能 • 17

Slide 18

Slide 18 text

カーネル • アプリケーションの処理を行うための基 本構造 18

Slide 19

Slide 19 text

カーネル • バンドル機構とWebアプリケーションの基 本処理フローの提供 19

Slide 20

Slide 20 text

ブログ記事 • PHPメンターズのブログにこのような話を 書いています http://phpmentors.jp/ 20

Slide 21

Slide 21 text

イテレーション 1 アプリケーションの 枠組みの準備 21 CREATING BUNDLE

Slide 22

Slide 22 text

Symfony2開発環境準備 • 日本Symfony ユーザー会 「Symfonyの インストールと 設定」 http://docs.symfony.gr.jp/ symfony2/book/ installation.html 22

Slide 23

Slide 23 text

$ php app/check.php 動作確認 • コマンドライン • Web http://localhost/ ~hidenorigoto/test/Symfony / web/ app_dev.php/ 23 ࠷ॳ͸ඞͣίϚϯυͰ ֬ೝ͢Δ͜ͱ

Slide 24

Slide 24 text

ファイル構成 24

Slide 25

Slide 25 text

appディレクトリ配下 25

Slide 26

Slide 26 text

srcディレクトリ配下 • 実際に自分のソースコードを配置する場 所 • 自分の名前空間 26

Slide 27

Slide 27 text

バンドルの作成 • コマンドライン 27 $ php app/console generate:bundle ͦ ͷ લ ʹ

Slide 28

Slide 28 text

バンドルの名前、名前空間 • 今から作ろうとしているものは何なのか • 関西PHPユーザーズグループの アンケートアプリケーション • 名前空間:KPHPUG • アプリケーション:Questionnaire • バンドル名:QuestionnaireBundle 28

Slide 29

Slide 29 text

ファイル構成 29

Slide 30

Slide 30 text

イテレーション 2 ページフローの構築と テンプレートの基礎 30 IMPLEMENTING PAGE FLOW

Slide 31

Slide 31 text

実装の流れを俯瞰 31

Slide 32

Slide 32 text

ページフローに必要なもの • ルーティング/ルート • コントローラから • テンプレートレンダリング • リダイレクト • コントローラ、テンプレートでルートに 基づくリンク • CSRFチェック用フォーム 32

Slide 33

Slide 33 text

実装するフロー 33 入力画面 確認画面 完了画面

Slide 34

Slide 34 text

実装するフロー 34 入力画面 確認画面 完了画面 準備 / GET inputAction /input inputPostAction POST confirmationAction confirmationPostAction /confirmation POST GET successAction /success GET ポストバック方式

Slide 35

Slide 35 text

ルート一覧 35 URL HTTP メソッド コントローラ ルート名 /input GET inputAction kphpug_questionnaire_answer_input /input POST inputPostAction kphpug_questionnaire_answer_inputpost /confirmation GET confirmationAction kphpug_questionnaire_answer_confirmation /confirmation POST confirmationPostAction kphpug_questionnaire_answer_confirmationpos t /success GET successAction kphpug_questionnaire_answer_success / GET indexAction kphpug_questionnaire_answer_index php app/console router:debug

Slide 36

Slide 36 text

Symfonyでのルート定義 • YAMLやXML、PHPファイルでの定義 • 従来のフレームワークに近い • アノテーション(DocBlockコメント)に よる定義 • どれを使っても同等 • 混在も可能 36 ルーティング http://docs.symfony.gr.jp/ symfony2/book/routing.html

Slide 37

Slide 37 text

実際のコード例 37 ルート定義 アノテーション リダイレクト メソッド テンプレート 描画

Slide 38

Slide 38 text

実際のコード例 38 ルートからURL生成 (Twig関数) テンプレートの基本(日本Symfonyユーザー会) http://docs.symfony.gr.jp/symfony2/book/ templating.html

Slide 39

Slide 39 text

空のフォーム 39 空のフォーム 作成 CSRFだけチェック される

Slide 40

Slide 40 text

テンプレート(Twig) • テンプレート継承を使ってレイアウト (親)→子のような関係を作れる • PythonのDjango、Jinja2由来の記法 40

Slide 41

Slide 41 text

従来のテンプレート 41 コントローラ テンプレート レイアウトテンプレート 埋め込み HTML 親子1階層

Slide 42

Slide 42 text

Twigテンプレート 42 コントローラ テンプレート 親テンプレート HTML

Slide 43

Slide 43 text

Twigテンプレート 43 {% block body %} {% endblock %} {% extends “layout.html.twig ”%} {% block body %}

見出し

コンテンツです。

{% endblock %} 親 子 子は何階層でも 作れる

Slide 44

Slide 44 text

テンプレート継承を使う例 44 ああああああああああああ ああああああああああああ ああああああああああああ ああああああああああああ あああ いいいいいいいいいいいい いいいいいいいいいいいい いいいいいいいいいいいい いいいいいいいいいいいい いいい うううううううううううう ううう うううううううううううう う ええええええええええ ええええええええええ layout.html.twig single.html.twig 2col.html.twig

Slide 45

Slide 45 text

イテレーション 3 モデルの準備 45 CREATING ENTITIES

Slide 46

Slide 46 text

モデルの役割 • 単に永続化(データベースの話)ではない • シンプルなアプリケーションでは、単な るデータ構造のみになるケースが多い • 業務の知識を組み込む場所 • エンティティ、リポジトリ、サービス • by DDD(ドメイン駆動設計) 46

Slide 47

Slide 47 text

Doctrine2 • http://www.doctrine- project.org/ • データマッパー型ORM (Metadata Mapping) • PofEAA • Domain Model • Data Mapper • Metadata Mapping • Unit of Work • Repository • マッピング定義はYAML、XML、ア ノテーションどれでも同等 47

Slide 48

Slide 48 text

Symfony2におけるモデル • フレームワークが直接モデルの機能を提 供しない • DoctrineBridge、DoctrineBundle およびFrameworkBundleによる利用 48

Slide 49

Slide 49 text

$ php app/console generate:doctrine:entity エンティティクラス作成 • コマンドライン 49

Slide 50

Slide 50 text

マッピング追加定義 • あくまで「マッピング」であることに注 意 • 「DBスキーマの定義」ではない • マッピングからDBテーブル群を生成す ることも可能 • ジェネレータでは指定できないリレー ションの部分を追加する 50

Slide 51

Slide 51 text

初期データの投入 • データ投入用コマンドを作る • バンドル配下のCommand ディレクトリに、Symfony \Bundle\FrameworkBundle \Command \ContainerAwareCommandク ラスを継承した HogeCommandクラスを作成 51

Slide 52

Slide 52 text

イテレーション 4 フォームの実装 52 CREATING FORM

Slide 53

Slide 53 text

Symfonyのフォーム • 送信されたデータの入れ物となるオブ ジェクト(エンティティクラス)と、各 項目の入力用のフィールドのマッピング を行うデータマッパー • FormType • 値の変換(DataTransformer) • バリデーション等の呼び出し 53

Slide 54

Slide 54 text

Formコンポーネント 54 コントローラ (Form)Type AnswerType FormBuilder Form FormView AnswerDetailsType

Slide 55

Slide 55 text

FormType 55 Person ・name ・address ・bloodType エンティティ フォーム PersonType ・text ・text ・choice

Slide 56

Slide 56 text

FormType 56 Person ・name ・address ・bloodType ・company エンティティ フォーム PersonType ・text ・text ・choice ・CompanyType CompanyType ・text ・text ・: Company ・name ・address ・:

Slide 57

Slide 57 text

動的にフォーム項目を定義 • 今回のアプリケーションは特殊 • 項目数が可変(実行時に決まる)た め、固定のFormTypeではダメ • FormTypeのbuildメソッド内で、フィー ルドのproperty_pathを調整する(マッ ピング先が決まる) 57

Slide 58

Slide 58 text

イテレーション 5 データの永続化 58 STORING DATA

Slide 59

Slide 59 text

永続化の基本 • エンティティマネージャオブジェクトを 取得 • エンティティマネージャへ永続化指示 • エンティティマネージャをflush 59 $em = $this->get(‘doctrine’)->getEntityManager(); $em->persist($answer); $em->flush();

Slide 60

Slide 60 text

More DDD Way • エンティティはリポジトリに保存する • リポジトリクラスにaddメソッドを実装 • addメソッド内でpersistするようにす る • 保存するオブジェクトの準備やエンティ ティマネージャのflushはサービスに実装 する 60

Slide 61

Slide 61 text

実際のコード例 61 リポジトリ

Slide 62

Slide 62 text

実際のコード例 62 サービス サービスクラスにユースケースを実装 トランザクション境界にもなる

Slide 63

Slide 63 text

イテレーション 6 ドメインモデルのテスト 63 TESTING DOMAIN MODELS

Slide 64

Slide 64 text

参考資料 • Symfonyドキュメン ト • PHPUnitドキュメン ト 64

Slide 65

Slide 65 text

ドメイン=自分のもの • 自分で好きなように書く • PHPUnit用のテストケースを書くのみ。 65 サービスクラスのユースケースを中心にテ ストを記述していく

Slide 66

Slide 66 text

サービスコンテナが必要? • FrameworkBundleのWebTestCaseを継承す る? • いろいろ弊害あり • ComponentFactoryを使う https://github.com/piece/stagehand- componentfactory 66

Slide 67

Slide 67 text

イテレーション 7 コントローラのテスト 67 TESTING DOMAIN CONTROLLER

Slide 68

Slide 68 text

コントローラのテスト • Symfonyドキュメントを参照 • WebTestCase • client、crawler 68

Slide 69

Slide 69 text

まじめに • コントローラのユニットテスト • ページフローが正しく動作しているか どうかをテスト • DB等にアクセスしない • モックオブジェクトで代用 69

Slide 70

Slide 70 text

イテレーション 8 サービス 70

Slide 71

Slide 71 text

サービス • サービスコンテナへ登録 • オブジェクトの構成に準備が必要な場合 の手間が軽減される(構成の知識を1箇 所に集約できる) • 例:コンストラクタがEntityManagerを 受け取る等 71 サービスコンテナ http://docs.symfony.gr.jp/symfony2/book/ service_container.html

Slide 72

Slide 72 text

参考資料 72

Slide 73

Slide 73 text

• PofEAA Patterns of Enterprise Application Architecture (Martin Fowler) 73

Slide 74

Slide 74 text

• DDD (ドメイン駆動設計) Domain Driven Design 74

Slide 75

Slide 75 text

75 • 次世代開発基盤技術“Software Factories”詳解 第3回 長期的な要求を定義するフィー チャ・モデル http://www.atmarkit.co.jp/fdotnet/ softfactory/softfactory03/ softfactory03_01.html

Slide 76

Slide 76 text

76 ありがとう ございました!