2015年10月14日水曜日

【Symfony2.7ドキュメント読んだ?】ルーティング

ルートの定義

# app/config/config.yml
framework:
    # ...
    router: { resource: "%kernel.root_dir%/config/routing.yml" }

# app/config/routing.yml
app:
    resource: "@AppBundle/Controller/"
    type:     annotation

基本的なルート設定

// src/AppBundle/Controller/MainController.php
// ...
class MainController extends Controller
{
    /**
     * @Route("/")
     */
    public function homepageAction()
    {
        // ...
    }
}

プレースホルダーを使ったルーティング

// src/AppBundle/Controller/BlogController.php

/**
 * @Route("/blog/{page}", defaults={"page" = 1})
 */
public function indexAction($page)
{
    // ...
}
URL Route Parameters
/blog blog {page}=1
/blog/1 blog {page}=1
/blog/2 blog {page}=2

プレースホルダーの値の条件

// src/AppBundle/Controller/BlogController.php
// ...
/**
 * @Route("/blog/{page}", defaults={"page": 1}, requirements={
 *     "page": "\d+"
 * })
 */
public function indexAction($page)
{
    // ...
}

/**
  * @Route("/blog/{slug}")
  */
public function showAction($slug)
{
    // ...
}
URL Route Parameters
/blog/2 blog {page}=2
/blog/my-blog-post blog_show {slug}=my-blog-post
/blog/2-my-blog-post blog_show {slug}=2-my-blog-post

ルーティングは、先に条件にマッチしたメソッドを実行する

// src/AppBundle/Controller/MainController.php
// ...
class MainController extends Controller
{
    /**
     * @Route("/{_locale}", defaults={"_locale": "en"}, requirements={
     *     "_locale": "en|fr"
     * })
     */
    public function homepageAction($_locale)
    {
    }
}
Path Parameters
/ {_locale} = "en"
/en {_locale} = "en"
/fr {_locale} = "fr"
/es won't match this route

HTTPメソッドの指定

/ src/AppBundle/Controller/MainController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class MainController extends Controller
{
    /**
     * @Route("/news")
     * @Method("GET")
     */
    public function newsAction()
    {
        // ... display your news
}
    /**
     * @Route("/contact")
     * @Method({"GET", "POST"})
     */
    public function contactFormAction()
    {
        // ... display and process a contact form
    }
}

ホスト名でコントローラを振り分け

# ホスト名を指定
mobile_homepage:
    path:     /
    host:     m.example.com
    defaults: { _controller: AcmeDemoBundle:Main:mobileHomepage }

homepage:
    path:     /
    defaults: { _controller: AcmeDemoBundle:Main:homepage }

# プレースホルダーを用いる場合
projects_homepage:
    path:     /
    host:     "{project_name}.example.com"
    defaults: { _controller: AcmeDemoBundle:Main:mobileHomepage }

homepage:
    path:     /
    defaults: { _controller: AcmeDemoBundle:Main:homepage }

# プレースホルダーを用いる場合(サブドメインを指定する)
mobile_homepage:
    path:     /
    host:     "{subdomain}.example.com"
    defaults:
        _controller: AcmeDemoBundle:Main:mobileHomepage
        subdomain: m
    requirements:
        subdomain: m|mobile

homepage:
    path:     /
    defaults: { _controller: AcmeDemoBundle:Main:homepage }

# パラメータを用いる場合
mobile_homepage:
    path:     /
    host:     "m.{domain}"
    defaults:
        _controller: AcmeDemoBundle:Main:mobileHomepage
        domain: "%domain%"
    requirements:
        domain: "%domain%"

homepage:
    path:  /
    defaults: { _controller: AcmeDemoBundle:Main:homepage }

# ホスト名で読み込むルーティング定義ファイルを指定する
acme_hello:
    resource: "@AcmeHelloBundle/Resources/config/routing.yml"
    host:     "hello.example.com"

高度なルーティングの例

/ src/AppBundle/Controller/ArticleController.php
// ...
class ArticleController extends Controller
{
/**
* @Route(
*        "/articles/{_locale}/{year}/{title}.{_format}",
*        defaults={"_format": "html"},
*        requirements={
*            "_locale": "en|fr",
*            "_format": "html|rss",
*            "year": "\d+"
*        }
*)
*/
    public function showAction($_locale, $year, $title)
    {
    }
}

# マッチするURLの例
/articles/en/2010/my-post
/articles/en/2010/my-post.rss
/articles/en/2010/my-latest-post.html

コントローラのネーミングパターン

bundle:controller:action
Bundle Controller Class Method Name
AppBundle BlogController showAction

ルーティングのテスト

$crawler = $client->request(
    'GET',
    '/homepage',
    array(),
    array(),
    array('HTTP_HOST' => 'm.' . $client->getContainer()->getParameter('domain'))
);

ルートマッチングの条件指定

contact:
    path:     /contact
    defaults: { _controller: AcmeDemoBundle:Main:contact }
    condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'"

ルートの確認

# ルートの一覧
php app/console debug:router

#  詳細
php app/console debug:router homepage

# マッチするルートの確認
php app/console router:match /blog/my-latest-post

関連サイト


前の記事 | 次の記事

2015年10月13日火曜日

【Symfony2.7ドキュメント読んだ?】コントローラー

リクエスト、コントローラ、レスポンスのライフサイクル

リクエストは、フロントコントローラ(app.php, app_dev.php)からアプリケーションが実行され、 RouterがURIを元にマッチするルートのコントローラを実行する。 コントローラがレスポンスオブジェクトを返し、HTTPヘッダー情報とレスポンスオブジェクトの内容を クライアントへ返す。

開発用フロントコントローラのIP制限解除

app_dev.phpの次のようにコメントアウトする
ローカルで開発する場合は不要
/* IP制限解除
if (isset($_SERVER['HTTP_CLIENT_IP'])
    || isset($_SERVER['HTTP_X_FORWARDED_FOR'])
    || !(in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1', '::1')) || php_sapi_name() === 'cli-server')
) {
    header('HTTP/1.0 403 Forbidden');
    exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
*/

アノテーションによるURLとコントローラのマッピング

// src/AppBundle/Controller/HelloController.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class HelloController
{
    /**
     * @Route("/hello/{name}", name="hello")
     */
    public function indexAction($name)
    {
        return new Response('Hello '.$name.'!');
    }
}

ベースコントローラの継承

よく使われそうなヘルパーメソッドが使えるようになる
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class HelloController extends Controller
{
    // ...
}

複数の引数

引数の順番は関係がなく、対応した変数名があることが重要
public function indexAction($firstName, $lastName, $foo = 'bar')
{
    // ...
}

リダイレクト

return $this->redirectToRoute('homepage'); // 引数はルート
// redirectToRoute is equivalent to using redirect() and generateUrl() together:
// return $this->redirect($this->generateUrl('homepage'), 301);

# デフォルトは302リダイレクト
return $this->redirect('http://symfony.com/doc');

return $this->redirectToRoute('homepage', array(), 301);

# 元はレスポンスオブジェクト
return new RedirectResponse($this->generateUrl('homepage'));

テンプレートのレンダリング

// renders app/Resources/views/hello/index.html.twig
return $this->render('hello/index.html.twig', array('name' => $name));

// renders app/Resources/views/hello/greetings/index.html.twig
return $this->render('hello/greetings/index.html.twig', array(
    'name' => $name
));

他のサービスのアクセス

$templating = $this->get('templating');

$router = $this->get('router');

$mailer = $this->get('mailer');

サービスの確認コマンド

cd project_name
php app/console debug:container

エラーと404レスポンスの管理

// 404
throw $this->createNotFoundException('The product does not exist');

// 500
throw new \Exception('Something went wrong!');

フラッシュメッセージ

# コントローラ
use Symfony\Component\HttpFoundation\Request;

public function updateAction(Request $request)
{
    $form = $this->createForm(...);
    $form->handleRequest($request);
    if ($form->isValid()) {
        // do some sort of processing
        $this->addFlash(
            'notice',
            'Your changes were saved!'
        );
        // $this->addFlash is equivalent to $this->get('session')->getFlashBag()->add
        return $this->redirectToRoute(...);
    }
    return $this->render(...);
}

# テンプレート
{% for flashMessage in app.session.flashbag.get('notice') %}
    
{{ flashMessage }}
{% endfor %}

リクエストオブジェクト

リクエストオブジェクトを使ってパラメータの値など取得できる
ルーティングパラメータにマッチしない変数名がある場合に、RuntimeExceptionが発生する
use Symfony\Component\HttpFoundation\Request;

// create a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);

// create a JSON-response with a 200 status code
$response = new Response(json_encode(array('name' => $name)));
$response->headers->set('Content-Type', 'application/json');
use Symfony\Component\HttpFoundation\Response;

public function indexAction(Request $request)
{
    $request->isXmlHttpRequest(); // is it an Ajax request?

    $request->getPreferredLanguage(array('en', 'fr'));

    $request->query->get('page'); // get a $_GET parameter

    $request->request->get('page'); // get a $_POST parameter
}

他のコントローラへフォワードする

public function indexAction($name)
{
    $response = $this->forward('AppBundle:Something:fancy', array(
        'name'  => $name,
        'color' => 'green',
    ));
    // ... further modify the response or return it directly
    return $response;
}

# forward先のコントローラ
public function fancyAction($name, $color)
{
   // 
}

関連サイト


2015年10月9日金曜日

【Symfony2.7ドキュメント読んだ?】パーミッションの設定

パーミッションの設定

Webサーバの実行ユーザーとディレクトリのパーミッションが同じ場合

特に設定変更なし

ACLをサポートしている場合

rm -rf app/cache/*
rm -rf app/logs/*
HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\  -f1`
sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs
sudo chmod +a "`whoami

ACLをサポートしている場合(chmod +aをサポートしていない場合)

# この記事ではこの方法で設定しました。
HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\  -f1`
sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs
sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs

ACLが使えない場合

# 次のファイルを修正する
# app/console
# web/app.php
# web/app_dev.php

# コメントを外す
umask(0002); // This will let the permissions be 0775

# または
umask(0000); // This will let the permissions be 0777

関連サイト


2015年10月7日水曜日

【Symfony2.7ドキュメント読んだ?】ビルトインWebサーバの使い方

ビルトインWebサーバの起動、停止、確認コマンド

cd project_name
php app/console server:run

# Webサーバ起動後に次のURLにアクセスします。
# http://localhost:8000/

# IPアドレスおよびポート指定で起動
php app/console server:run 192.168.0.1:8080

# バックグラウンドで実行させたい場合
php app/console server:start
php app/console server:start 192.168.0.1:8080

# サーバの停止
php app/console server:stop
php app/console server:stop 192.168.0.1:8080

# サーバ状態の確認
php app/console server:status
php app/console server:status 192.168.0.1:8080

# 環境変数やルータースクリプトの指定
php app/console server:start --env=test --router=app/config/router_test.php

# ドキュメントルートを指定する
php app/console server:start --docroot=public_html

関連サイト


2015年10月6日火曜日

【Symfony2.7ドキュメント読んだ?】インストールと更新

Symfonyインストーラのインストール

Linux、MacOSの場合

sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony
sudo chmod a+x /usr/local/bin/symfony

Symfonyアプリケーションの作成

Linux、MacOSの場合

# [project_name]はプロジェクト名です。適宜変更してください。
# この記事ではこのコマンドを使用してインストールしました。
symfony new project_name

# インストールするSymfonyのバージョンを指定する場合
symfony new project_name 2.6
symfony new project_name 2.6.5

# LTSバージョンの場合 ※1
symfony new project_name lts

# composerを使ってSymfonyアプリケーションを作成する場合
composer create-project symfony/framework-standard-edition project_name

# バージョン指定の場合
composer create-project symfony/framework-standard-edition project_name "2.6.*"
※1 LTSは36ヶ月サポートするようです。 バージョン毎のサポート期間は、こちらで確認できます。

composerのインストール

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

1行の場合
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
SymfonyはWebサーバ機能があるのでインストール後すぐに動作確認できます。

Symfonyアプリケーションの更新

# 依存関係ライブラリのインストール
cd  project_name
composer install

# 更新
cd  project_name
composer update

関連サイト



【Symfony2.7ドキュメント読んだ?】 動作検証環境

Symfony2.7の公式ドキュメントを読んでメモを残します。

公式ドキュメント


この記事は、次の環境で動作確認します。

  • CentOS 6.7

  • PHP 5.6.14

  • nginx/1.8.0

  • Symfony 2.7