Symfony Best Practice - 実践編 Chapter5-3-
前回
いじった結果は随時GitHubにあげていきます。
ParamConverterを使う
BlogControllerでParamConverterを使う場合と、使わない場合の書き方の比較をしてみる。
変更前は、ParamConverterを使っている。
// BlogController.php /** * @Route("/comment/{postSlug}/new", name="comment_new") * @Method("POST") * @Security("is_granted('IS_AUTHENTICATED_FULLY')") * @ParamConverter("post", options={"mapping": {"postSlug": "slug"}}) */ public function commentNewAction(Request $request, Post $post)
そもそもParamConverterは何をしてくれるかドキュメントで確認。 ParamConverterにはDoctrine ConverterとDateTime Converterの2種類があって、今回はDoctrine Converterを使ってるということがわかった。
Doctrine ConverterによりpostSlug
というパラメーターでコントローラーに渡されてきた値を、$post
のslug
とマッピングさせて値の比較をしている。
ということは、slug
というパラメーターでコントローラーに渡してあげればParam Converte使わなくても動きそうなので、以下のように書き換えてみる。
// BlogController.php /** * @Route("/comment/{slug}/new", name="comment_new") * @Method("POST") * @Security("is_granted('IS_AUTHENTICATED_FULLY')") */ public function commentNewAction(Request $request, Post $post)
パラメーターはTwig側で設定されてるので、Twigの方も書き換える。
// /app/Resources/views/blog/_comment_form.html.twig {{ form_start(form, { method: 'POST', action: path('comment_new', { 'slug': post.slug }) }) }}
これでParamConverterを使わなくても動くようになった。 ただし、ドキュメントによると以下の条件の下で実現してるそうな。
- ルーティングに
{id}
が設定されている場合は、find ()
メソッドで主キーをフェッチ。 - ワイルドカードに指定されているプロパティで、エンティティにあるプロパティを
findOneBy()
でフェッチ。
上記挙動はoptionsを指定すると変えられる。
複雑なことをしたい場合は、Entityクラスに対応するRepositoryクラスを作って、@Entity
アノテーションでRepositoryクラスのメソッドを指定して検索させることができる。
感想
「EntityとRepositoryの違いって何?」と思っていた時期があったけど、この挙動を知ると役割の違いが明確化できていいな。 Best Practiceでは、Twigをデザイナーさんに触ってもらうことを考えているので、Entityでの名称を画面に持ち込みたくない(意識させたくない)場合とかに使ってねという意図を感じなくもない。
Symfony Best Practice - 実践編 Chapter5-2-
前回
いじった結果は随時GitHubにあげていきます。
あえて@Template
アノテーションを使ってみる
ベストプラクティスでは「使ってはいけない」とされている@Template
アノテーションをあえて使ってみる。
なるほど。すっきりはするけど、Responseオブジェクトを返しているというよりは、arrayを返しているようにしか見えないから、「なんでこれで画面が表示されるんだろう?」となるのは理解出来る。
しかも、アノテーション(SensioFrameworkExtraBundle)のドキュメントによると、コントローラーがResponseオブジェクトを返す場合は、@Template
の設定内容が無視されるらしい。
本当かどうか、やってみた。
// BlogController /** * @Route("/", defaults={"page": "1"}, name="blog_index") * @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="blog_index_paginated") * @Method("GET") * @Cache(smaxage="10") * @Template("blog/index.html.twig") */ public function indexAction($page) { dump($page); $posts = $this->getDoctrine()->getRepository(Post::class)->findLatest($page); return $this->render('blog/test.html.twig', ['posts' => $posts]); // return ['posts' => $posts]; }
レスポンスにするテストページは以下のようにした。
{% extends 'base.html.twig' %} {% block body_id 'blog_index' %} {% block main %} <h1>This is test page</h1> {% endblock %} {% block sidebar %} {{ parent() }} {{ show_source_code(_self) }} {% endblock %}
実行結果
アノテーションよりResponseオブジェクトが優先されていることが確認できました。
Symfony Best Practice - 実践編 Chapter5-
前回
いじった結果は随時GitHubにあげていきます。
コントローラーのactionに渡されるパラメータをのぞく
まずはデバッグしてパラメータをのぞき見してみる。
// AppBundle\Controller\BlogController public function indexAction($page) { dump($page); // ~ }
てやると、
こんな風に出る。 2ページ目はどうかな。
え。。パラメーター文字列になるんだ。。ボタンで移動したときは文字列になって、トップページから1ページ目に遷移したときだけ数値になってるぽい。
ルーティングを見ると理解できる。デフォルトのルーティングはpage
が数値で設定されていて、/page
がルーティングに入ってきたときは文字列にしている。
// AppBundle\Controller\BlogController /** * @Route("/", defaults={"page": 1}, name="blog_index") * @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="blog_index_paginated") * ~ */ public function indexAction($page)
気持ち悪いから、デフォルトも文字列にしておこうっと。
/** * @Route("/", defaults={"page": "1"}, name="blog_index") ~ */ public function indexAction($page)
せっかくなので、本家にPRだしてみよう。マージされたら嬉しいな。
Symfony Best Practice - 実践編 Chapter3-
せっかくBest Practiceを翻訳したので、手を動かしながら中を見ていきます。 Chapter2まではプロジェクトの初期設定なので、割愛します。
いじった結果は随時GitHubにあげていきます。
DB接続設定
デフォルトではSQLiteを使うように設定されていますが、実際のお仕事ではMySQLを使うことが多いと思いますので、MySQLに切り替えます。
(積読消化。正月休みにがっつり読む予定。)
- 作者: Baron Schwartz,Peter Zaitsev,Vadim Tkachenko,菊池研自,株式会社クイープ
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/11/25
- メディア: 大型本
- この商品を含むブログ (7件) を見る
- 作者: Bill Karwin,和田卓人,和田省二,児島修
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/01/26
- メディア: 大型本
- 購入: 9人 クリック: 698回
- この商品を含むブログ (45件) を見る
閑話休題。修正が必要なファイルは以下の2箇所。
# app/config/parameters.yml # 削除 database_url: 'sqlite:///%kernel.root_dir%/data/blog.sqlite' # 追加 database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null
# app/config/config.yml # Doctrine Configuration (used to access databases and manipulate their information) doctrine: dbal: # if you don't want to use SQLite, comment the two following lines # driver: "pdo_sqlite" # path: "%kernel.root_dir%/data/blog.sqlite" # uncomment the following lines to use a database different than SQLite driver: pdo_mysql host: "%database_host%" port: "%database_port%" dbname: "%database_name%" user: "%database_user%" password: "%database_password%" charset: UTF8
デフォルトでMySQLの設定がされてるので、良心的。
ここで以下のコマンドを流すと、app/config/parameters.yml
のdatabase_name
で指定した名前のデータベースが作成されます。
$ php bin/console doctrine:database:create
さて、画面はどうなるかな。
あ。。。データがないのか。。。
app/config/config.yml
でコメントアウトした"%kernel.root_dir%/data/blog.sqlite"
を読んでるっぽい。
SQLiteの中身をダンプして確認。
$ sqlite3 ./app/data/blog.sqlite SQLite version 3.8.5 2014-08-15 22:37:57 Enter ".help" for usage hints. sqlite> .tables symfony_demo_comment symfony_demo_post symfony_demo_user sqlite> .output ./dump.txt sqlite> .dump sqlite> .output stdout sqlite>.exit
以下を参照して、SQLiteのダンプをMySQLに突っ込む。
$ mysql -uroot symfony < target.sql ERROR 1064 (42000) at line 2: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CLOB NOT NULL, authorEmail VARCHAR(255) NOT NULL, publishedAt DATETIME NOT NULL,' at line 1
MySqlにCLOB型がないみたいなので、target.sql
のCLOBをTEXTに変更
$ mysql -uroot symfony < target.sql ERROR 1064 (42000) at line 2: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NOT NULL, authorEmail VARCHAR(255) NOT NULL, publishedAt DATETIME NOT NULL, PRIM' at line 1
NOT DEFERRABLE INITIALLY IMMEDIATE
の指定がMySQLだと効かないみたいなので、この部分を削除して、SET FOREIGN_KEY_CHECKS=0;
を追加。
詳細はapp/data/target.sql
参照
これでMySQLでデモアプリが表示された。
アプリに関する設定
app/config/
配下のconfig.yml
について。「環境ごとに設定を書き換えられるように」Symfonyが以下を用意してくれている。
このコンセプトに反するから、「変更されることが予想されいてない設定は定数として定義しましょう。」ということ。
config_prod.yml
は本番環境の設定config_dev.yml
は開発環境の設定config_test.yml
はファンクショナルテスト用の環境設定。ブラウザからはアクセスできない。*1
環境設定ファイルの読み込みは、AppKernel.registerContainerConfiguration()
で実行されている。
「semantic dependency injection」とはなんだろう
まずは辞書的な「semantic」の意味を調べる。
セマンティックとは、一般的には「意味」や「意味論」に関することを指す語である。IT用語としては、コンピュータに文書や情報の持つ意味を正確に解釈させ、文書の関連付けや情報収集などの処理を自動的に行わせる技術について用られる語である。
う〜ん。わからん。Best Practiceでは*Extension
を使うのを「semantic dependency injection」と言っているので、「How to Load Service Configuration inside a Bundle」をチェック。
Symfonyでは、様々なサービスを使っていることに気づくでしょう。これらのサービス群は、あなたのアプリのapp/config/
ディレクトリに登録されていることでしょう。しかし他のプロジェクトから、そのバンドルを使いたくなったときには、バンドル自体にサービス設定が内包されていた方が良いでしょう。
ふむ。バンドル単体を公開したい人に向けたBestPracticeですかね。バンドルの利用者が設定をごにょごにょしなくても済むように、バンドルの中にサービスの設定を閉じ込めておこうという趣旨であることは理解できました。
Symfony Meetup #15参加記録
Symfony Meetup#15に参加してきました。
Symfony Best Practice座談会
前半の座談会では、昨日まで翻訳していたBest Practiceを使っていただけました。 お役に立ててよかったです。
数カ所、訳がわかりにくいところあったので、写経しながら直していきます。(アドベントカレンダーまでにやる。)
LT大会
@malloc007 さん
LT1: @malloc007 さん 電子回路のはなし#symfony_meetup pic.twitter.com/Uoggr464vV
— 腹周りたぷたぷ (@hanahiro_aze) 2016年11月6日
学びの広げ方の考察がすごいよかった。 Ajax呼び出しの仕組みに感動して、フロントエンドからサーバサイドまでのレイヤー間の連携について理解したのに飽き足らず、 CPUの回路にまで理解を広げようとする貪欲さ本当にすごい。見習いたい。
@polidog さん
さっきの資料公開しました。https://t.co/ACrwpcxZI1#symfony_meetup
— polidog@SSRがんばる (@polidog) 2016年11月6日
Bundleを作る話。 app.phpからSymfonyでリクエストを処理している一連のの流れを一回おさらいしたくなった。 アドベントカレンダー投稿し終わったらやる。
LT3:@ganchiku さん ユニットテストの話
— 腹周りたぷたぷ (@hanahiro_aze) 2016年11月6日
#symfony_meetup
Unitテストの守備範囲について議論。 どんなテストで何を担保したいのかを意識する必要性を再認識。
メモ
SRP(Single Responsibility Principal) Unitテストの範囲外:API/DB/Mail/ファイルシステムと接続する・スレッドをまたぐ
RequestStackのInject TokenStrageInterfaceのInject メソッドチェーンをしないようにサービスをインジェクトする。オブジェクトが知っているべき範囲を明確にする。 ユニットテストを書いているから安心してリファクタリングできない場合もあるのではないか?(振る舞い) ユニットテストの改善とリファクタリングは表裏一体になるのかについて議論
@ttskch さん
LT4: @ttskch さん FTPで直接触りたい人たちとの共生のはなし
— 腹周りたぷたぷ (@hanahiro_aze) 2016年11月6日
#symfony_meetup pic.twitter.com/kFff7jrI8w
サーバーで直接触ったコードをgitにcommitする仕組みのはなし。
アフタートーク
BuisinessLogicが何を指すのか。についての議論。 BestPracticeではwikipeiaの記事にリンクが貼ってあって、BuisinessLogic = CRUDに関わるものという理解。
今回も学びの多い、自分の理解不足を痛感する勉強会でした。