Titelbild zum Artikel

Erstellen einer flexiblen Projektstruktur für Symfony2 und AngularJS

Avatar von Robin Gloster

Symfony2 im Backend und AngularJS im Frontend, das war die Wahl für ein internes Projekt zur Teamplanung bei meinem Praktikum im September/Oktober 2013.

Eine besondere Herausforderung von Zusammenfügen der beiden Frameworks, sodass sie angenehm miteinander harmonieren, da beide ihr eigenes Routing mitbringen.
Soll hier Symfony das Routing übernehmen oder soll AngularJS das alles regeln? Brauche ich eine spezielle Webserverkonfiguration?

In diesem Artikel möchte ich beschrieben, wie die Projektstruktur aussehen muss, damit Symfony2 und AngularJS bequem zusammen spielen und es keine Kompilkationen mit Updates und dem Deployment gibt.

Prinzipielles Vorgehen

Nach einigem Ausprobieren und Gesprächen im Team habe ich mich entschlossen, wie folgt vorzugehen:

  • den Webserver so zu konfigurieren, damit er den ersten Teil des Routings übernimmt
  • alle Anfragen nach /api/* an den Symfony2 Front-Controller zu leiten
  • eine statische Datei auszuliefern, damit sie über die URL gefunden werden kann
  • AngularJS One-Page-App auszuliefern wenn an der URL keine Datei gefunden wird

Dies sieht am Beispiel von nginx folgendermaßen aus:

server {
  listen                *:80;
  server_name           example.com;

  location ~ ^/api.+$ {
    root /path/to/project/api/web;
    index app.php;
    rewrite ^(.+)$ /app.php/$1 last;
  }

  location ~ ^/app\.php($|/) {
    root /path/to/project/api/web;
    index app.php;
    fastcgi_buffer_size 512k;
    fastcgi_buffers 16 512k;
    fastcgi_busy_buffers_size 512k;
    fastcgi_index app.php;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    include fastcgi_params;
  }

  location / {
    root /path/to/project/dist;
    try_files $uri $uri/ /index.html =404 ;
    index index.html;
  }
}

Die Teile der Konfiguration dabei sind:

  • location ~ ^/api.+$ schreibt die Requests von /api/* an die app.php um
  • location ~ ^/app\.php($|/) leitet die Requests der app.php an php-fpm weiter
  • location / probiert erst mit try_files die Datei und leitet sonst an AngularJS weiter

Keine Probleme mit Updates von Composer & Co.

Die Ordnerstruktur im Projekt gleicht grundsätzlich dem Angular Template von Yeoman, mit Symfony in einem Unterordner:

  • /app (Angular-Sourcedateien für Entwicklung)
  • /dist (gebaute JS Dateien für Produktivumgebung)
  • /api (Symfony Hauptordner)

Somit ist die Struktur flexibel, Grunt, Bower etc. funktionieren dann wie in jedem anderen Yeoman-Projekt, Composer wird einfach im Symfony-Ordner ausgeführt.

Deployment mit Capistrano

Ein weiterer interessanter Teil des Setups war das Deployment mit Capifony, einem Addon für Capistrano für Symfony. Da Symfony nicht wie erwartet im Hauptordner des Repositorys liegt, war die Konfiguration eine echte Herausforderung.

Zu guter Letzt sind aber nur ein paar Änderungen an der Konfiguration notwendig, um auch dieses Problem aus der Welt zu schaffen. Hier ist eine Übersicht der wichtigsten Änderungen an der Standardkonfiguration für Symfony2:

  • set :cache_path, "api/app/cache"
  • set :composer_options, "--no-dev --verbose --prefer-dist --optimize-autoloader --working-dir=api"
  • set :composer_dump_autoload_options, "--optimize --working-dir=api"
  • set :symfony_console, "api/app/console"
  • shared_files / shared_children auf die richtigen Pfade setzen

Für das Deployment des Javascript-Codes ist das Capistrano Plugin für Grunt hilfreich. Mit einigen weiteren Änderungen lassen sich noch npm und Bower ausführen:

before 'deploy:finalize_update', 'grunt'
before 'grunt', 'bower:install'
before 'bower:install', 'npm:install'

set :grunt_tasks, ['build']

namespace :bower do
  desc "Install bower components"
  task :install do
    run "cd #{release_path} && bower install --production --quiet"
  end
end

namespace :npm do
  desc "Install npm components"
  task :install do
    run "cd #{release_path} && npm install --quiet"
  end
end

Fazit

Die hier gezeigten Lösungen haben sich im Projekt bewährt, da die Konfiguration im Gegensatz zu anderen Varianten keinen speziellen Code oder große Änderungen an weiteren Config-Dateien nach sich zieht. Dadurch bleibt das Projekt übersichtlich und auch für andere Entwickler verständlich und es ergibt sich die Möglichkeit Tools wie Capistrano zu benutzen, um bspw. automatisiert zu deployen oder testen.

Ausblick
Aufgrund der Webserver-Konfiguration, muss irgendwie sichergestellt werden, dass alle Entwickler im Team die gleiche Basisumgebung haben. Darüber hinaus sollte es für neue Entwickler im Team möglich sein, gleich mit der Arbeit zu beginnen und keine Zeit verschwenden zu müssen um z.B. die Infrastruktur einzurichten oder sich um deren Abhängigkeiten Gedanken zu machen. Hier könnte Vagrant der beste Lösungsansatz sein, um den Entwicklern das Leben ganz einfach zu erleichtern- denn mit Vagrant lassen sich schnell virtuelle Maschinen (inkl. aller benötigten Komponenten) erstellen, die dann das gesamte Team nutzen kann, so dass alle mit exakt den gleichen Bibliotheken und Infrastrukturkomponenten in identischen Versionen arbeiten.

Avatar von Robin Gloster

Kommentare

4 Antworten zu „Erstellen einer flexiblen Projektstruktur für Symfony2 und AngularJS“

  1. Erstellen einer flexiblen Projektstruktur für Symfony2 und AngularJS http://t.co/ZM2gOHerVO via @mayflowerphp

  2. Erstellen einer flexiblen Projektstruktur für Symfony2 und AngularJS http://t.co/Yuuu4l4i8e #symfony

  3. Avatar von Sándor, Bolla
    Sándor, Bolla

    wird eine zweite Artikle stattfinden wo die Frage: „Soll hier Symfony das Routing übernehmen oder soll AngularJS das alles regeln?“ beantwortet wird? Die Frage wurde nur teilweise beantwortet – denke ich zumindesst -, und ich bin nicht im Bilde wie AngularJS Router funktioniert deswegen meine Frage

  4. Avatar von Robin Gloster
    Robin Gloster

    Diese Frage wird im Artikel schon beantwortet, wenn auch nicht explizit. Hier vielleicht noch die kurze Erklärung dazu.

    Da nginx je nach URL schon auf Symfony oder Angular leitet, kommt es gar nicht mehr zu dem genannten Problem, da beide Frameworks nur für die direkt dafür bestimmten Requests zuständig sind.

    Das heißt alle Requests die /api voranstehen haben gehen direkt an Symfony ohne das Angular je etwas davon mitbekommt.
    Alle sonstigen Requests (statische Dateien ausgenommen) werden vom Router in Angular behandelt, hier wird Symfony gar nicht angesprochen.

    Also wird das schon gehandhabt bevor es überhaupt zu den Framework-spezifischen Routern kommt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.