FOSJsRoutingBundleを使ってみよう!

FOSJsRoutingBundleを使ってみよう!

December 4, 2014,
tags: symfony php advent calendar


このエントリーをはてなブックマークに追加

Symfony Advent Calendar 2014 - Qiita の5日目の記事です。

SymfonyでBackboneJSやAngularJSを利用したアプリケーションを使うときってどうやって作ってますか?
僕の場合はサクッと作りたい場合はFOSJsRoutingBundleとFOSRestBundleを利用しています。
今日はそんなFOSJsRoutingBundleについてさっくりと書いていきたいと思います。

そもそもFOSJsRoutingBundleとは?

SPA(シングルページアプリケーション)な環境でSymfony2を利用してる場合に、Js側でもルーティング周りを統一できるBundleです。 Tiwgでpath()関数と同じ機能をJavaScript側にも提供してくれる非常に便利なBundleです。

FOSJsRoutingBundleを使わない場合どうなるのか?

まずは、FOSJsRoutingBundleを使わない例を考えてみましょう。
ここに2つほど使わないサンプルを書いてみました。

そもそもSymfony側のルーティングを気にしない

Symfony側のルーティング名を使わなくてもリクエストを送ることはできますよね。
URLを直接書いてしまっても大きくは問題がありません。

// src/MySpaMainBundle/Resources/Default/index.html.twig

<script>
var DefaultModel = Backbone.Model.extend({});

var DefaultCollection = Backbone.Collection.extend({
  model: DefaultModel,
  methodToUrl: {
    "read": "/collection"
  },
  sync: function(method, model, options) {
    options = options || {};
      options.url = (this.methodToUrl[method.toLowerCase()] || options.url).replace('id', model.get('id'));
      Backbone.sync(method, model, options);
    }
  }
});

var collection = new DefaultCollection();

collection.fetch({
  success: function(res) {
    console.log(res)
  }
});
</script>

しかし、この方法だと実装を進めていくなでいろいろ不便になってきます。
パラメータの扱い方が変わってURLが変更になったりしたらもう・・・。

じゃあ無理やりTwigのpath関数を利用する

Twigファイルの中にJsが書かれていた時に限定されてしまいますが、以下のように書くことができます。

// src/MySpaMainBundle/Resources/Default/index.html.twig
<script>
var DefaultModel = Backbone.Model.extend({

});

var DefaultCollection = Backbone.Collection.extend({
    model: DefaultModel,
    methodToUrl: {
        "read": "{{ path("myspamain_default_collection") }}"
    },
    sync: function(method, model, options) {
        options = options || {};
        options.url = (this.methodToUrl[method.toLowerCase()] || options.url).replace('id', model.get('id'));
        Backbone.sync(method, model, options);
    }
});

var collection = new DefaultCollection();
collection.fetch({
    success: function(res) {
        console.log(res)
    }
});


</script>

直接URLを記載するやり方よりはマシなんですが・・・。
簡単なアプリケーションだったら一つのファイルの中にすべてのコードを実装することもできますが、さすがにちょっと大きい規模になってきたらJsのファイルを分割して書きたくなると思います。 しかし、ファイルを分けてしまうとpath関数が使えなくなるし、、、ちょっと辛いですね。

ちなみに今だから言えるんですが、実際のプロダクトでこのような実装していました(´・ω・`)
もちろん今は全てFOSJsRoutingBundleをつかようにしていますけどねw

FOSJsRoutingBundleをつかってみよう

結局ファイルを分割しなきゃいけないような状況だとFOSJsRoutingBundleは非常に便利です。
JS側でSymfonyのルーティング名を取得できるます。

例えば上のサンプルのようなmyspamain_default_collectionってルーティング名をjs側で取得する場合は以下のようにします。

Routing.generate('myspamain_default_collection');
// will result in /collection

パラメータが必要な場合は普通にオブジェクトを渡してあげれば大丈夫です。

Routing.generate('myspamain_default_collection',{page: 1});

簡単に導入方法を説明します。
まずはcomposer でライブラリを用意します。

$ ./composer.phar require friendsofsymfony/jsrouting-bundle

入ったらAppKernelに追加

// app/AppKernel.php
public function registerBundles()
{
    return array(
        // ...
        new FOS\JsRoutingBundle\FOSJsRoutingBundle(),
    );
}

あとは assets:installを実行する

$ app/console assets:install

これで準備は完了です。 実際の使い方は以下のような感じになりますよ。

// src/MySpaMainBundle/Resources/Default/index.html.twig
<script>
var DefaultModel = Backbone.Model.extend({

});

var DefaultCollection = Backbone.Collection.extend({
    model: DefaultModel,
    methodToUrl: {
        "read": Routing.generate('myspamain_default_collection');
    },
    sync: function(method, model, options) {
        options = options || {};
        options.url = (this.methodToUrl[method.toLowerCase()] || options.url).replace('id', model.get('id'));
        Backbone.sync(method, model, options);
    }
});

var collection = new DefaultCollection();
collection.fetch({
    success: function(res) {
        console.log(res)
    }
});
</script>

JavaScript側でRouting.generate('myspamain_default_collection');やって実装できるのは便利ですね。
フロントエンド触る側からしたら、バックエンド側でどのような処理が行われているかも気にしなくていいし、このレベルならSymfony触れないフロントエンドの人にも使ってもらえるような気がします。

Backbone.routerとの連携のお話

まあ以前にも書いているんですが、Symfony2とBackbone.routerを併用していた場合にapp_dev.phpからアクセスした場合にうまく動作しません。 正しくいうとpushStateする場合にちゃんと動作しないです。

jsに無理やりprod環境だとかdev環境だとかの環境変数を渡すこともできるんですが、それもやっぱ避けたい。 そんなときはBackbone.history.startでrootのパスの値を指定できるので以下のように書けば大丈夫です。

Backbone.history.start({
  pushState: true,
  root: Routing.getBaseUrl().replace("/","") + "/"
});

最後の最後に

ちょっと話が逸れてしまいますが、SPAっぽい感じの開発していた時に便利だったChromeのプラグインも紹介しておきます。

2.6になったらいらないんですが、2.6以下のバージョン使用している人はぜひ使ってみてください。
Ajaxで通信している内容のプロファイラの画面へ飛べるので非常に便利です。

最後に

SymfonyとJavaScriptでそこそこ大きい規模の開発をする場合はRouting.generateがほんと便利なので、ぜひ使ってみてください。

おしまい。

comments powered by Disqus