Skip to content

Instantly share code, notes, and snippets.

@thinkadoo
Forked from evillemez/gist:8024315
Created May 12, 2014 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkadoo/aa12705e1a58519ae904 to your computer and use it in GitHub Desktop.
Save thinkadoo/aa12705e1a58519ae904 to your computer and use it in GitHub Desktop.

Project Deployment with Symfony2 & Angular.js

Symfony2 and Angular.js make a great, robust combintaion. Integrating them isn't immediately obvious, however. Both communities come with their own standards and suite of tools for managing the development process, and sometimes those tools conflict.

On the Symfony side, you mainly deal with composer for managing your dependencies, a few Symfony commands in your project, and any other tools you add into the mix, like phpunit.

On the Angular side, you'll probably end up using npm, bower and probably grunt for automating some of the pain.

The short answer

My team found a pretty good solution for integrating the two frameworks, in a word - don't. What we ended up doing, which has worked out very well, is to maintain the Angular-based UI project, and the SF2-based server API as separate codebases entirely. This allowed both projects to evolve independently of each other, be separately deployable, and more importantly, be independently testable. Also, both projects have their own suite of tools designed for that type of project, and they work the way you would expect without modification or conflict. You can follow the best practices of both communities without worry of conflicts.

Deploying

This type of setup makes your server setups a little more complicated - but not terribly so. Really, you can solve the issues of managing the servers, and deploying both the Angular side, and the Symfony side in one word - Ansible. If you aren't using Ansible, stop reading this and go check it out. If you are using Chef, Puppet, Capistrano, etc... - I can pretty much guarantee that anything you are doing with those, you can do in Ansible, and in the end will be more simple. For people used to configuring SF2 apps in YAML, it feels particularly familiar.

We actually maintain a 3rd codebase for the project, which is just the ansible repo that contains all the server configuration and app deployment information for the entire thing. Our ansible playbooks are also defined such that we can deploy everything at once, or deploy the ui side, or the server side independently of each other. This also lets us easily switch out which repo/branch to deploy, so deploying to a separate staging server to try out experimental features is incredibly simple.

I think the biggest benefit we saw in this setup was that as soon as we put our app in front of users, we got a lot of feedback about certain UI changes they wanted. It was trivial for us to go make those changes, tag a new version of the UI project, and then use ansible to just deploy the UI. The Symfony part of the project knew nothing about any of this, and didn't need to.

The technical issue you have to deal with in this type of setup is knowing which http requests go to the SF2 project, and which are just html/js/css served from disk in the angular project. In our case, all of the Symfony routes are prefixed under /api/, so it was easy to configure. Hammerspace is the codename we use for the project internally, it is divided into two subdirectories, hammerspace-server/ for the Symfony side, and hammerspace-ui/ for the Angular side. Here's an example of our nginx config we use in production (slightly modified for this post):

server {
  listen 1080;
  server_name example.org;
  root /var/hammerspace;
  set_real_ip_from 127.0.0.1;
  real_ip_header X-Forwarded-For;
  client_max_body_size 200m;
  
  #SF2 api routes
  location ~* ^/(api|apidoc|openid|_profiler)(/|$) {
    port_in_redirect off;
    rewrite ^ /hammerspace-server/web/app.php?$query_string break;

    include fastcgi_params;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_param SERVER_PORT 443;
    fastcgi_param HTTPS ON;
  }

  #SF2 file uploads for our project
  location /uploads {
    alias /var/hammerspace/hammerspace-server/web/uploads/;
  }

  #SF2 bundle static files, only really used for api docs
  location /bundles {
    alias /var/hammerspace/hammerspace-server/web/bundles/;
  }

  #Angular UI, basically everything else
  location / {
    alias /var/hammerspace/hammerspace-ui/dist/;
  }

  error_log /var/log/nginx/hammerspace.error.log;
  access_log /var/log/nginx/hammerspace.access.log;
}

Other

That's the short story for how to deploy a hybrid setup where Symfony2 and Angularjs are kept separate. I have a lot to stay about the developer workflow for this type of setup - mainly that it's a vast improvement over everything else I've ever experienced. But, that's a separate story that I'd be happy to detail if there is interest. For now, here are a few details as to why the workflow improved for us:

We are able now to evolve the UI and Server codebases independently. Our API can remain completely agnostic about the UI, which means it's a clean codebase for us to start re-implementing legacy features in a non-hacky way.

Our UI project is actually independently testable. We built some angular tools that we use for faking the API data structures and routes - which allowed us to prototype the UI very quickly - but also prototype the API quickly. This let us nail down what the server API actually needed to do, which in a few case led us to rethink our years-old data model, which was great. It also allowed us to demo the UI for people even when there wasn't a working server.

If anyone is interested in the details of how this all worked from a developer perspective, let me know and I'm happy to share the details. Our team did one hell of a trial-by-fire on this, and it all worked out amazingly well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment