Issei.M's Techlog

Web/iOS エンジニアの僕が技術関連のメモ等をつらつらと。主に Symfony について書いています。

[Symfony] JSON レスポンスを返す

    /**
     * @Route("/test.json", name="json")
     */
    public function jsonAction()
    {
        // do something

        return new JsonResponse(array('id' => 12345, 'title' => 'json test'));
    }

バージョン 2.1 で追加された JsonResponse をコントローラ内で返すだけです。コンストラクタの引数に渡した配列がそのまま出力されます。
Content-Type も自動で application/json にしてくれます。更に、JSONP のコールバック処理もできたりします。
※詳しくは JsonResponse

とっても便利☆

[Symfony] サブドメイン別にルーティングを制御する

Symfony 2.2 で正式に追加された機能の様です。
フロントエンドをexample.com、バックエンドをsystem.example.comとしてそれぞれ別のバンドルで運用する場合の設定例をご紹介。

まずはバックエンド用のバンドルを作成します。

$ php app/console generate:bundle --namespace=Acme/BackendBundle --format=annotation

続いて上記コマンドで自動追記されたBackendBundleへのルーティングを修正します。

# app/config/routing.yml

# バックエンド
system:
    resource: "@BackendBundle/Controller/"
    type:     annotation
    prefix:   /
    host:     system.example.com # 追加!

# フロントエンド
homepage:
    resource: "@FrontendBundle/Controller/"
    type:     annotation
    prefix:   /

新しく追加されたhostによってサブドメインのルーティングを制御できるようになりました。これでsystem.example.comはすべてBackendBundleのコントローラにルーティングされます。
とっても簡単!

[Symfony][Capistrano] Capifony で Symfony2 のアプリケーションをデプロイする

今回は Symfony アプリを Capistrano で簡単にデプロイできる Capifony (http://capifony.org) の使い方を紹介します。

実行環境は Ruby 1.9.1 + Capistrano 2.14.2 です。

Capifony をインストールする

RubyGems でインストールします。

$ gem install capifony

続いてデプロイする Symfony プロジェクト上で capifony コマンドを実行し、イニシャライズします。

$ cd /path/to/own_sf2pj
$ capifony .

これで Capifileapp/config/deploy.rb の2ファイルが自動生成されました。 更に次のコマンドで、実行できるタスクを確認してみます。

$ cap -T

しかしここで以下のエラーが…。

"raise_if_conflicts': Unable to activate capifony-2.2.7, because capistrano-2.14.2 conflicts with capistrano (<= 2.14.1, >= 2.13.5)"`

どうやら Capistrano 2.14.2 では動作しないみたいなので、仕方なく 2.14.1 にダウングレードします。

$ gem uninstall capistrano
$ gem install capistrano -v 2.14.1

これで無事、正常に利用できるようになりました。

deploy.rb を設定する

僕はとりあえずこんな感じで設定しました。かなり基本的な事のみ設定しています。

今回 Apache ユーザに app/cacheapp/logs への書き込み権限を ACL で与えていますが、環境によっては ACL が使えないこともあるので、その場合は chownchmod も利用できるみたいです。

デプロイ前の準備をする

以下のコマンドを実行します。

$ cap deploy:setup

これで必要なディレクトリ構造が構築されます。ここで一旦 SSH でデプロイ先サーバに入り、本番用の parameters.yml を作ります。

$ mkdir -p /var/www/example.com/shared/app/config
$ vim /var/www/example.com/shared/app/config/parameters.yml

これで準備 OK です。

そしてデプロイへ...

ローカルに戻り、以下のコマンドを実行します。

$ cap deploy

これで正常にデプロイが完了しました!

因みにこの Capifony、symfony 1.4.x でも利用できるらしいスグレモノです。未だに symfony 1.4.x 系のプロジェクトをいくつか抱えているのでそちらでも試してみようと思います。

[Doctrine][MySQL] アノテーションで unsigned を定義する

Doctrine2 のエンティティプロパティに unsigned を設定する方法が分かりづらかったのでメモ。

/**
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 * @ORM\Column(type="bigint", options={"unsigned"=true})
 */
protected $id;

こんな感じで Column::options プロパティを設定します。因みに SQL をダンプすると次のような結果になります。

CREATE TABLE users (id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, ...

また type と options に使える値は次のクラスに定義されています。(MySQLの場合)

Doctrine\DBAL\Schema\MySqlSchemaManager

※ Column::columnDefinition で決め打ちしてしまうと、外部キー周りでハマりやすいので極力避けた方がよさげです。

[Symfony][Doctrine] Fixtures からサービスコンテナを取得する

Fixtures 内で、サービスコンテナを呼び出したくなる事はしばしばあります。

例えば User エンティティクラスが Security コンポーネントの UserInterface を実装している場合、PasswordEncoder を使いたくなりますね。 しかしそのままでは AbstractFixture クラス内でサービスコンテナが取得できないので、PasswordEncoder の取りようがありません。

そんな時は、AbstractFixture に ContainerAwareInterface を実装してしまいましょう。

// src/Acme/UserBundle/DataFixtures/ORM/LoadUserClassData.php

namespace Acme\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Acme\UserBundle\Entity\User;

// ↓追加
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class LoadUserClassData extends AbstractFixture
    implements OrderedFixtureInterface, ContainerAwareInterface // ←追加
{
    ///**
     * @var ContainerInterface
     */
    private $container;

    /**
     * {@inheritDoc}
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    // ~
}

これで load が実行される前にサービスコンテナが格納されます。
実際に load 時に使用する場合は次の様な感じです。

public function load(ObjectManager $manager)
{
    $factory = $this->container->get('security.encoder_factory');

    $user1 = new User();
    $user1->setUserName('issei');
    $user1->setSalt(hash('sha512', mt_rand(111111, 999999)));

    $encoder = $factory->getEncoder($user1);
    $user1->setPassword($encoder->encodePassword('hogehoge', $user1->getSalt()));
}

詳しくは公式のドキュメントを参照
http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#using-the-container-in-the-fixtures