As a part of our Free Dojo Support initiative, we received the following question from Alexander about the best way to use Dojo with a Symfony application:

The Question

“I’m looking to use Dojo with Symfony2 to build a modular back end user interface. It would feature a single action that kicks off a parse-on-load border layout with an accordion on the left hand side. Each modular section – for the users’ sake, I call “areas” – would have free-run over what goes on in the main content region of the border layout.

Unrelated – but only to a degree – on the Symfony side, I was planning on creating a class that represents an area that can be tagged as such, with a few abstract controller methods.

When the back end is accessed, the dependency injection container could be queried for that tag, returning all classes which are then called for their respective accordion and content code. The code they return in this case would be dojo markup found inside of twig templates. They’re then all put together and shipped out at once.

The first appeal that came to mind with this approach is that server-side, each instance of an area can decide whether the user is eligible for the interface (or portions of it) and either opt out or tailor some data & script to be included with their dojo markup. Second is that at no point in time are the natural features of dojo (or Symfony, I hope!) subverted. I end up leveraging the templating from Symfony as well as all of Dojo’s power.

The problem is: I don’t know if this is a sane approach! Set me straight! ;)”

Thanks for writing, Alexander! The high-level advice for using Symfony with Dojo is basically the same as it is for using Dojo with any other server-side framework: when it comes to rich Internet applications, do as much as possible on the client. That means that Symfony should be used primarily as the endpoint for a dojo/data or dojo/store data store, or a dojo/rpc service, instead of being used to generate markup and pass rendered HTML to the client. By moving as much processing as possible to the client, your application will be faster, more scalable, and more responsive.

Symfony as a data store

RESTful data stores are particularly useful for applications that have a consistent, object-oriented data model. Dojo 1.6 and later come with a JSON REST store that can be used, and Symfony makes this especially easy to implement thanks to its ORM and routing systems. Because Symfony 2 is such a new framework, there isn’t a lot of information yet about how to leverage its new features to easily create RESTful interfaces. In the meantime, much of the existing information for Symfony 1 can be adapted, such as this slideshow about RESTful interfaces in Symfony 1.

On the Dojo side, the recently released tutorial on the Dojo Object Store and the related reference guide documentation should give you all the information you need to start working with a JSON REST store on the client.

(Keep in mind that you still need to ensure that your server is continuing to validate data, even if you add validation on the client-side, to prevent malicious users from adding invalid data to your database. Symfony’s validation system makes this trivial, even if you aren’t building forms on the server.)

Symfony as a service

For remote calls that don’t fit into a REST paradigm, simple RPC services can be used instead. With Service Mapping Descriptions (SMD), you can also create well-defined interfaces for those interactions, though Symfony doesn’t have any mechanism for digesting SMDs at present. More information about using SMDs will be coming soon; until then, our 2008 blog post about SMD provides an overview of the format and how it is used with the dojox/rpc/Service module. Even without a full SMD, dojox/rpc/Service can still be used to perform RPC calls—you just miss out on the validation of requests against the SMD’s defined schema.

Within Symfony, creating a basic JSON RPC service implementation is as simple as creating a new action inside of an appropriate controller that returns JSON in response to a request from the client. Since RPC services don’t need to send and receive the same data type, and it’s easier to handle incoming data in PHP if it’s a URL-encoded string, we can send data from the client in a application/x-www-form-urlencoded format and return a response in an application/json format by using the “URL” envelope when creating our service on the client:

require([ "dojox/rpc/Service" ], function(Service){
	var service = new Service({
		transport: "GET",
		envelope: "URL",
		services: {
			test: {
				target: "/rpc/test"
			}
		}
	});

	service.test({ // makes a GET request /rpc/test?foo=foo&bar=bar
		foo: "foo",
		bar: "bar"
	}).then(doStuffWithResponse); // doStuffWithResponse will be passed the evaluated JSON object
});

Templates on the client

Dojo has an extremely versatile templating system that we’ve discussed several times over the years. The dijit/_Templated tutorial also does a good job of going over the basics of creating and using templated widgets. One thing that the tutorial fails to mention, however, is that you can actually replace the default dijit templating system with other JavaScript templating engines by swapping out the _Templated._stringRepl function. This means that if you already have a large number of widget templates written in Twig, you could create a Twig engine in JavaScript and continue to use the same templates on the client. Here’s an example of a custom templating language using modified Underscore.js syntax:

define([ "dojo/_base/declare", "dojo/_base/lang", "dijit/_Widget", "dijit/_Templated" ], function(declare, lang, _Widget, _Templated){
  var _templateCache = {};

  return declare([ dijit._Widget, dijit._Templated ], {
    // these templates always have variable data in them and don't
    // use the default style for notating variables so stop _Templated
    // from trying to cache the resulting nodes
    _skipNodeCache: true,

    // modified (faster) Underscore.js templating
    _stringRepl: function(/*string*/ tmpl){
      var func = _templateCache[tmpl] = (_templateCache[tmpl] ||
        new Function('', 'var __p=[];__p.push(\'' +
        tmpl.replace(/\\/g, '\\\\')
            .replace(/'/g, "\\'")
            .replace(/<%=([\s\S]+?)%>/g, function(match, code) {
              return "'," + code.replace(/\\'/g, "'") + ",'";
            })
            .replace(/<%([\s\S]+?)%>/g, function(match, code) {
              return "');" + code.replace(/\\'/g, "'")
                                 .replace(/[\r\n\t]/g, ' ') + "__p.push('";
            })
            .replace(/\r/g, '\\r')
            .replace(/\n/g, '\\n')
            .replace(/\t/g, '\\t')
            + "');return __p.join('');"));

      return lang.trim(func.call(this));
    }
  });
});

A controller for single-page apps

The piece that has been missing from everything that we’ve discussed so far is how to actually pull this all together into a working application. The Application Controller recipe does a good job of doing this, and illustrates the fundamentals of launching and managing the lifecycle of a single-page RIA.

In your specific case, based on the information provided, your index page would probably provide some very basic HTML that outlined the layout for your application, then immediately kick off a request to retrieve information from the server about how to populate the accordion menu based on the currently logged in user. Once the UI is ready, the appropriate individual widgets will take over, responding to user interaction and loading other data into the main content area as necessary. If you’re creating dynamic forms, taking a look at our previous free support question might be worthwhile.

Try to avoid doing anything computationally intensive when serving the index page, like hitting a database or checking ACLs; the faster a user sees a rendered page, even if it is incomplete, the more favorably they will perceive the speed of the application. Making an extra request to the server might seem counter-intuitive and unnecessary, but by intelligently splitting your code into layers and taking advantage of parallel HTTP requests, your server can be busy processing the request while the client is still retrieving other required assets.

Conclusion

Dojo is one of the best toolkits for creating highly dynamic rich Internet applications, but using it to its fullest requires a significant shift in thinking. The traditional “server does everything” type of development that is typified by PHP frameworks like Symfony is excellent for building traditional Web sites, but it doesn’t do a good job of meeting the needs of today’s Web applications. Keeping the server out of the business of building views establishes better separation of concerns, reduces development time, and improves performance.

Happy coding!

SitePen Support

Have a Dojo question you’re just dying to ask? Get your free Dojo support now! Or sign-up for the Best JavaScript and Dojo Support available to get all of your questions answered all the time!