polidog lab++

Blog
Symfony2とBackboneJSを使うときに覚えておくと便利な3つの事

Symfony2とBackboneJSを使うときに覚えておくと便利な3つの事

Mar 22, 2014 | tech | js php BackboneJS symfony2

最近のJavaScript界隈のフレームワーク的な物だとAngularJSが人気ですが、僕はあえてBackboneJSを愛用しています。
ただ今のプロジェクトをBackboneJSで書いているだけなんですけどね。

いろいろと便利な機能満載なBackboneJSですが、Symfony2と組み合わせる場合にどうしたらいいの?って思う人もいるのではないでしょうか?
まあ海外のSymfony2の情報を見れば情報が記載されたサイトが沢山あるわけですが、ここ半年ぐらいで僕が試した中で、この方法使うと良いのではと思った事が3つあります。

  1. FOSJsRoutingBundleを利用する
  2. FOSRestBundleを利用したRestコントローラの作成
  3. Backbone.RouterでpushStateする場合のちょっとした設定

1. FOSJsRoutingBundleを利用する

BackboneJSのModelとかCollectionのURL指定をする際に、どのようにするのか?
悩む人は多いのではないでしょうか?

この場合いくつかのアプローチがあると思います。

FOSJsRoutingBundleを利用しないで無理矢理がんばる方法

  1. その1 JavaScript含めてすべての処理をTwigのテンプレートの中に記述する
    簡単なシングルページアプリケーションであれば、Twigテンプレートの中にすべてのjsを記述してしまえばいいので、特にModelやCollectionのURLのしていを悩む必要は無いと思います。 URLの指定はtwigの関数でもある{% raw %}{{ path(“指定先”) }}{% endraw %}を利用すれば、良い訳ですね。
<script>
  var HogeModel = Backbone.Model.extend({
    methodToURL: {
      'read'  : '{{ path("get_hoge") }}',
      'create': '{{ path("post_hoge") }}',
      'update': '{{ path("put_hoge", {"id": "id"}) }}',
      'delete': '{{ path("delete_hoge", {"id": "id"}) }}'
    },
    defaults: function() {
      return {};
    },
    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 HogeCollection = Backbone.Collection.extend({
    model: HogeModel,
    url: '{{ path("get_hoges") }}',
  });  
</script>

この方法も簡単なアプリケーションならやってもいいですが、ある程度大きい規模になると辛い物がありますよね・・・

  1. グローバルな変数に無理矢理URLを格納する方法 上記の方法と似ていますが、JavaScriptのファイルを別ファイルにしたい場合に使える技です。
    ただ、これも微妙で無理矢理Twig側でURLを吐き出して、JavaScriptのグローバルな変数に突っ込んでしまうという微妙な実装。。。
<script>
var Urls = {
  model: {
    'read'  : '{{ path("get_hoge") }}',
    'create': '{{ path("post_hoge") }}',
    'update': '{{ path("put_hoge", {"id": "id"}) }}',
    'delete': '{{ path("delete_hoge", {"id": "id"}) }}'
  },
  collection: {
    'url': '{{ path("get_hoges") }}'
  }
}
</script>
<script src="/js/hoge-model.js">
<script src="/js/hoge-collection.js">
  var HogeModel = Backbone.Model.extend({
    methodToURL: {
      'read'  : Urls.model.read',
      'create': Urls.model.create',
      'update': Urls.model.update',
      'delete': Urls.model.delete'
    },
    defaults: function() {
      return {};
    },
    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 HogeCollection = Backbone.Collection.extend({
    model: HogeModel,
    url: Urls.collection.url
  });  

ファイルを分割出来るという点ではこの方法にもメリットがある程度はありますが、やはり良くない。。。。

FOSJsRoutingBundleを利用したアプローチ

上記二つの方法は無理矢理感満載でしたが、FOSJsRoutingBundleならもっといい感じにURLの設定ができます。 FOSJsRoutingBundleをつかえば、FOSJsRoutingBundleの用意しているJavaScript側でRoutingオブジェクトをつかってURLの生成が行えます。
composerにFOSJsRoutingBundle指定してあげて、composer installを行ってください。

...
"friendsofsymfony/jsrouting-bundle": "1.5.3"

インストールが終わった後はassets installを行ってください。

次にrouting.ymlに以下を追記します。

fos_js_routing:
    resource: "@FOSJsRoutingBundle/Resources/config/routing/routing.xml"

あとはtwigにFOSJsRoutingBundleのrouter.jsを読み込むだけです。

<script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
<script src="/js/hoge-model.js">
<script src="/js/hoge-collection.js">

さらにhoge-model.jsとhoge-collection.jsも書き換えましょう。

  var HogeModel = Backbone.Model.extend({
    methodToURL: {
      'read'  : Routing.generate("get_hoge"),
      'create': Routing.generate("post_hoge"),
      'update': Routing.generate("put_hoge"),
      'delete': Routing.generate("delete_hoge")
    },
    defaults: function() {
      return {};
    },
    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 HogeCollection = Backbone.Collection.extend({
    model: HogeModel,
    url: Routing.generate("get_hoges")
  });  

2. FOSRestBundleを利用したRestコントローラの作成

BackboneJSのModelやCollectionからのリクエストはJSON形式でレスポンスしなければいけませんよね。
これって普通にコントローラを利用していた場合だと結構めんどくさい。。。

<?php
namespace Hoge\HogeBundle\Controller

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HogeContorller extends Contorller
{
    public function jsonAction()
    {
      $response = new JsonResponse();
      $response->setData([
          'data' => 123
      ]);
      return $response;
    }  
}
?>

てかあまり美しくない方法かと個人的には思います。しかもサンプルの場合はオブジェクトをレスポンスしてるからいいんですが、これがEntityとかオブジェクトになってしまった場合は本当にめんどくさい。。。

そんなときは、FOSRestBundleを利用しましょう。 まずはcomposer.jsonに以下のように記述しましょう。。

...
"friendsofsymfony/rest-bundle": "1.0.*@dev"
{% endcodeblock %}
あとはcomposer installしてください。まあいつも通りの作業ですね。  
次にconfig.ymlに以下を追記してください。
{% codeblock lang:yaml app/config/config.yml %}
fos_rest:
    routing_loader:
        default_format: json
    view:
        view_response_listener: force

あとはサンプルのようにコントローラを書きましょう。

<?php
namespace Hoge\HogeBundle\Controller

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Routing\ClassResourceInterface;

class HogeContorller extends Contorller
{
    /**
     * @Rest\View
     */
    public function cgetAction()
    {
      return [
        'data': 123
      ]
    }  
}
?>

これでEntityオブジェクトをリターンしても普通にjsonで返してくれますよー。

3. Backbone.RouterでpushStateする場合のちょっとした設定

Backbone.RouterをつかったときのURLパスを指定するときに、開発環境(app_dev.php)からアクセスしちゃうとうまくRouterが動作してくれない問題です。
これは解決策が至って簡単です。

まあだいたい思いつく方法としては、twigで無理矢理jsに今のenvがprodかdevなのかの情報を渡すってことを考える人もいるでしょう。 でもこの方法ってかっこわるいよね。

じゃあどうするのか?FOSJsRoutingBundleを使っていれば簡単に解決できますよ。以下のように。

$(function(){
  router = new HogeRouter();
  Backbone.history.start({
    pushState: true,
    root: Routing.getBaseUrl().replace("/","") + "/"
  });
});

app_dev.phpを使用してた場合は、Routing.getBaseUrl()で「/app_dev.php」と出力してくれるので、このような方法で、開発環境でも本番環境でもちゃんと動作してくれるようになります。

まとめ

  • FOSJsRoutingBundleを使う
  • FOSRestBundle
  • Router使う場合にはrootの指定をRouting.getBaseUrl()使ってする

まあ他にもasseticで管理するより、grunt使った方が良いよって話もありますが、その話はまた今度します。

comments powered by Disqus

関連記事

© 2017 polidog lab++