こんにちは。@mrtryです。 もう10月ですね。入社して半年か...。 残りの半年もがんばっていきたいと思います!
さて、「Symfony2入門」3回目の投稿です。 前回は、Symfony2での処理の流れについてまとめました。 今回は、その中で出てきた、ルーティングについて書きたいと思います。
以下、今回の目次になります。
- ルーティングとは
- ルーティングの設定方法
- 設定されているルーティングの確認
- ルーティングの設定例
ルーティングとは
ルーティングとは、リクエストされたURLに対して呼び出すアクションを決定する仕組みのことです。
例えば、/
というパスにアクセスがあったらhomepageAction
を実行する、というように対応付けする仕組みです。
Symfonyでは、ルーティングの設定次第で、対応付けを任意に設定することができます。
ルーティングの設定方法
ルーティングの設定は、YAML、XML、PHP、Annotationで設定できます。 今回の記事では、利用頻度が高いYAMLとAnnotationでの設定方法を紹介します。
YAML
Symfony2の設定ファイルは概ねYAMLで記述されています。
app/config
以下を確認するだけでも、config.yml
やparameters.yml
などのYAMLファイルが確認できます。
ルーティングの設定も、YAMLファイルで設定されています。
YAMLについて馴染みがない方は、以下のページを読むと書き方を理解できるかと思います!
例によって、symfonyのデモアプリケーションを見てみます。
設定ファイルは、app/config/routing.yml
にあります。
以下の箇所の設定では、/
というパスにアクセスがあった時、
default/homepage.html.twig
を引数にとり、FrameworkBundle
のTemplateController
にあるtemplateAction
を実行するという設定が書かれています。
ルーティング名はhomepage
となっています。
homepage: path: /{_locale} requirements: _locale: '%app_locales%' defaults: _controller: FrameworkBundle:Template:template template: default/homepage.html.twig _locale: '%locale%'
Annotation
コントローラにAnnotationを書くことでルーティングの設定をすることもできます。 ベストプラクティス的には、Annotationを用いたルーティングの設定方法がオススメされています。
Annotationを利用するには、設定ファイルにAnnotationでルーティングするという設定をする必要があります。
例によって、symfonyのデモアプリケーションのapp/config/routing.yml
を見てみます。
resource
でAnnotationが記述されているコントローラがあるディレクトリを指定し、type
でAnnotation
を設定します。
app/config/routing.yml
app: # @を先頭につけるとBundle以下からの相対パスで指定できます。 resource: '@AppBundle/Controller/' type: Annotation prefix: /{_locale} requirements: _locale: '%app_locales%' defaults: _locale: '%locale%'
設定後、コントローラにてルーティングの設定を行うことができます。
/blog
、/blog/page/{page}
にアクセスがあった時、このコントローラのindexAction()
を呼び出すという設定がされています。
ルーティング名にはそれぞれ、blog_index
、blog_index_paginated
となっています。
src/AppBundle/Controller/BlogController.php
/** * Controller used to manage blog contents in the public part of the site. * * @Route("/blog") * * @author Ryan Weaver <weaverryan@gmail.com> * @author Javier Eguiluz <javier.eguiluz@gmail.com> */ class BlogController extends Controller { /** * @Route("/", defaults={"page": 1}, name="blog_index") * @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="blog_index_paginated") * @Method("GET") * @Cache(smaxage="10") */ public function indexAction($page) { $posts = $this->getDoctrine()->getRepository(Post::class)->findLatest($page); return $this->render('blog/index.html.twig', ['posts' => $posts]); }
設定されているルーティングの確認
consoleでdebug:route
を実行すると、設定されているルーティングを確認することができます。
Name
を見ていくと、先ほど紹介したhomepage
、blog_index
、blog_index_paginated
が確認できます。
$ bin/console debug:route -------------------------- ---------- -------- ------ ---------------------------------------- Name Method Scheme Host Path -------------------------- ---------- -------- ------ ---------------------------------------- _wdt ANY ANY ANY /_wdt/{token} _profiler_home ANY ANY ANY /_profiler/ _profiler_search ANY ANY ANY /_profiler/search _profiler_search_bar ANY ANY ANY /_profiler/search_bar _profiler_info ANY ANY ANY /_profiler/info/{about} _profiler_phpinfo ANY ANY ANY /_profiler/phpinfo _profiler_search_results ANY ANY ANY /_profiler/{token}/search/results _profiler ANY ANY ANY /_profiler/{token} _profiler_router ANY ANY ANY /_profiler/{token}/router _profiler_exception ANY ANY ANY /_profiler/{token}/exception _profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css _twig_error_test ANY ANY ANY /{_locale}/_error/{code}.{_format} admin_index GET ANY ANY /{_locale}/admin/post/ admin_post_index GET ANY ANY /{_locale}/admin/post/ admin_post_new GET|POST ANY ANY /{_locale}/admin/post/new admin_post_show GET ANY ANY /{_locale}/admin/post/{id} admin_post_edit GET|POST ANY ANY /{_locale}/admin/post/{id}/edit admin_post_delete DELETE ANY ANY /{_locale}/admin/post/{id} blog_index GET ANY ANY /{_locale}/blog/ blog_index_paginated GET ANY ANY /{_locale}/blog/page/{page} blog_post GET ANY ANY /{_locale}/blog/posts/{slug} comment_new POST ANY ANY /{_locale}/blog/comment/{postSlug}/new security_login ANY ANY ANY /{_locale}/login security_logout ANY ANY ANY /{_locale}/logout homepage ANY ANY ANY /{_locale} -------------------------- ---------- -------- ------ ----------------------------------------
ルーティングの設定例
ルーティングの設定例を4パターン紹介します。
- URLとコントローラを1対1で対応付け
- ワイルドカードを利用した対応付け
- 正規表現を利用した条件指定
- HTTPメソッドの指定
それぞれのパターンで、YAMLとAnnotationの設定例を紹介します。 なお、Annotationでルーティング設定する際に必要なYAMLの設定は省略しています。
URLとコントローラを1対1で対応付け
URLとコントローラを1対1で対応付けする設定です。
以下の例では、/
にアクセスがあったら、
AcmeDemoBundle
のMainController
に定義されているhomepageAction()
を呼び出す設定をしています。
YAML
YAMLで設定すると、以下のようになります。
_welcome: path: / defaults: _controller: AcmeDemoBundle:Main:homepage
一番最初のkeyはルーティングに登録されるルート名です。
設定されたルート名はconsole debug:route
すると、Nameの欄に表示されます。
今回の設定だと、最初にkeyに_welcome
が設定されているので、これがそのままルート名になります。
ルート名以下は、ルーティングの設定になります。
マッチするパスの指定は、path
に設定します。
今回は、/
にアクセスがあった際のルーティングを設定するので、path: /
と書きます。
プレースホルダの初期値、特殊なルーティングパラメーターの設定する際には、defaults
以下に書きます。
プレースホルダの初期値の設定については次の設定例で紹介し、この設定例では特殊なルーティングパラメーターの設定について紹介します。
特殊なルーティングパラメーター(Special Routing Parameter)とは、Symfonyで用意されている便利に使えるパラメータです。
_controller
_format
_locale
の3種類がこれに該当します。
指定したパスで実行するコントローラを指定するには、特殊なルーティングパラメーターである_controller
に論理コントローラ名
を設定します。
論理コントローラー名は、バンドル名:コントローラ名:アクション名
という文字列で表現されます。
今回の場合、呼び出したいのはAcmeDemoBundleのMainControllerに定義されているhomepageAction()
なので、
AcmeDemoBundle:Main:homepage
が論理コントローラとなります。
注意点として、コントローラ名はController
、アクション名はAction
をそれぞれ省略する必要があるので、注意しましょう。
Annotation
同じ設定をAnnotationで設定すると、以下のようになります。
class MainController extends Controller { /** * @Route("/", name="_welcome") */ public function homepageAction() { ... } }
Annotationでの設定は、Doucument Commentに@Route()
というAnnotationを書くことで設定できます。
マッチするURLの指定は、( )
内に書き、ルート名はname=""
で指定をすることができます。
Annotationでルーティングを設定する際は、
_contorller
の指定は、Annotationを書いてあるコントローラが呼び出されるので、必要はありません。
ワイルドカードを利用した対応付け
/blog/1
、/blog/2
、/blog/3
のようなパスにマッチするルーティングを設定しようとした時、
そのパスごとに設定を書いていたのでは、とても手間です。
内容が同じようなものを表示するのであれば、/blog/*
というようにまとめて指定できたらとても便利です。
これは、/blog/{page}
のようなプレースホルダを設定することで、マッチさせることができます。
以下の例では、
/blog/{page}
というパスにアクセスがあった場合、AcmeBlogBundle
のBlogController
のindexAction()
を実行する設定をしています。
YAML
YAMLで設定すると、以下のようになります。
blog_index: path: /blog/{page} defaults: _controller: AcmeBlogBundle:Blog:index page: 1
パスにプレースホルダを設定する際は、{プレースホルダ名}
という書き方で設定できます。
今回の例では、page
という名前でプレースホルダを設定しています。
defaults以下では、プレースホルダの初期値を設定しています。
今回の例では、page
には初期値として1
を設定しています。
この設定により、/blog/
という感じにpageの箇所が空
のパスにアクセスがあった場合、defaultsで指定した値が利用されpage = 1
としてコントローラで処理が行われます。
また、設定したプレースホルダは、実行するコントローラの引数に設定することで、値をそのまま利用することができます。 例として、以下の様に書くことでメソッド内で値を利用できます。
public function indexAction($page) { ... echo $page; // '/blog/{page}' のpageの値が返ってくる ... }
Annotation
同じ設定をAnnotationで設定すると、以下のようになります。
/** * @Route("/blog") * */ class BlogController extends Controller { /** * @Route("/{page}", name="blog_index", defaults={"page": 1}) * */ public function indexAction($page) { ... } }
先ほどのAnnotationの設定と少し変わり、classに対しても@Route("/blog")
と指定がされています。
こうすることで、class以下に定義されるURLに/blog
というパスを付与することができます。
なので、indexAction()
に設定されているURLは/{page}
としか書かれていませんが、実際には/blog/{page}
と一致します。
なお、YAMLではprefix
を用いて設定することができます。
正規表現を利用した条件指定
/blog/{page}
というように、まとめて指定する方法を紹介しました。
ですが、前回の設定だけだと、page
に対して値のチェックをしていないので、整数以外も取りうる可能性があります。
page
の値を正規表現でチェックし、整数のみに一致するルーティングの設定に修正します。
以下の例では、
/blog/{page}
というURLにアクセスがあった場合、AcmeBlogBundle
のBlogController
のindexAction()
を実行します。
ただし、page
の値は長さが1以上の整数
、という制限を設定しています。
YAML
YAMLで設定すると、以下のようになります。
blog_index: path: /blog/{page} defaults: _controller: AcmeBlogBundle:Blog:index page: 1 requirements: page: \d+
条件指定は、requirements
で指定し、ネストして定義してある変数名を挙げることで設定できます。
page
は整数にしぼりたいので、正規表現で長さが1以上の整数
という指定をしています。
Annotation
同じ設定をAnnotationで設定すると、以下のようになります。
/** * @Route("/blog") * */ class BlogController extends Controller { /** * @Route("/{page}", requirements={"page" = "\d+"}, name="blog_index",defaults={"page": 1}) * */ public function indexAction($page) { ... } }
HTTPメソッドの指定
APIを実装した際などに、あるURLにアクセスするHTTPメソッドを指定したい時があります。 HTTPメソッドの指定も、ルーティングの設定で制限することができます。
以下の例では、/contact
にアクセスがあった時
GET
なら、AcmeDemoBundle:Main:contact
POST
なら、AcmeDemoBundle:Main:contactProcess
を呼び出すという設定をしています。
YAML
YAMLで設定すると、以下のようになります。
contact: path: /contact defaults: _controller: AcmeDemoBundle:Main:contact methods: [GET] contact_process: path: /contact defaults: _controller: AcmeDemoBundle:Main:contactProcess methods: [POST]
HTTPメソッドを制限したいときは、methods
を用いて、配列でGET
、POST
を指定することができます。
Annotation
同じ設定をAnnotationで設定すると、以下のようになります。
class MainController extends Controller { /** * @Route("/contact", name="contact") * @Method("GET") * */ public function contactAction() { ... } /** * @Route("/contact", name="contact_process") * @Method("POST") * */ public function contactProcessAction() { ... } }
@Method()
というAnnotationを書くことで、メソッドの指定ができます。
おわりに
「Symfony2入門」の3回目として、ルーティングについて紹介しました。 次回は、コントローラについて基本的なことをまとめたいと思います。