こんにちは。@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:contactPOSTなら、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回目として、ルーティングについて紹介しました。 次回は、コントローラについて基本的なことをまとめたいと思います。