Symfony2: Bundle your libraries!

Avatar von Paul Seiffert

In vielen Software-Projekten kommen Entwickler irgendwann an einen Punkt, an dem Funktionalität implementiert wird, die in anderen Projekten wiederverwendet werden können soll. Nachdem ich in der jüngeren Vergangenheit von mehreren Kollegen gefragt wurde, wie man dies in Symfony2-Projekten bewerkstelligen kann, wollte ich schriftlich eine Zusammenfassung der wichtigsten Punkte bereitstellen. Das Resultat ist dieser Blog-Artikel, der eine Schritt-für-Schritt-Anleitung enthält, wie man wiederverwendbaren Code kapselt und in einem eigenen Symfony2-Bundle bereitstellt.

Das Wissen, welches ich hier verpacke, habe ich teils aus der Symfony2-Dokumentation, teils aus der Erfahrung, welche ich in den letzten Jahren in Symfony2-Projekten gesammelt habe. Nachdem ich des Öfteren Library-Code aus Projekten ausgelagert hatte, konnte ich einige Patterns identifizieren und vor allem feststellen, dass diese Vorgehensweisen in Symfony2-Projekten (abgesehen von syntaktischen Eigenheiten) denen in beliebigen anderen Umgebungen sehr stark ähneln.

Die folgende Liste ist als Template zu verstehen, welches in einfachen Projekten genau so verwendet werden kann, in komplexeren jedoch wahrscheinlich erweitert werden muss.

    1. Werde Dir darüber klar, welche Teile wirklich wiederverwendbar werden sollen.
      Zu oft findet man „Libraries“, die so „generisch“ geschrieben sind, dass sie genau in einem Projekt Anwendung finden. Gute Libraries konzentrieren sich darauf, entweder genau eine Funktionalität oder eine API für eine externe Abhängigkeit bereitzustellen. 
    2. Definiere alle in Punkt 1 identifizierten Klassen als Services.
      An allen Stellen, an denen solche Klassen verwendet werden, sollten sie per Dependency Injection bereitgestellt werden. Eine Ausnahme stellen Value-Objects dar. Diese können beispielsweise durch eine Factory (welche wiederum als Service definiert ist) erzeugt werden.
      Der Sinn dieses Schritts ist es, die Schnittstellen zu der Library-Funktionalität explizit zu machen. Dies ist als Vorbereitung für das Verschieben des Codes notwendig. 
    3. Erstelle das neue Bundle
      Erstelle jetzt ein neues Bundle im Ordner src, in dem der Library-Code später unterkommt: 

                  ./app/console generate:bundle

      Im Folgenden bezeichne ich das neue Bundle LibraryBundle und das ursprüngliche, in dem das restliche Symfony2-Projekt lebt ProjectBundle.

    4. Verschiebe den Quellcode in das LibraryBundle
      Jetzt musst Du den in Punkt 1 identifizierten Code in das LibraryBundle verschieben. Danach müssen die Service-Definitionen der umgezogenen Klassen aus dem ProjektBundle in das LibraryBundle umgezogen werden. Wenn sich dadurch die Namen der Services ändern, müssen im ProjectBundle die Referenzen angepasst werden.
    5. Zwischenziel:
      Das Projekt sollte zu diesem Zeitpunkt ohne Einschränkung funktionieren! (Wo sind Deine Tests?) 
    6. Erstelle ein neues Git-Repository für das LibraryBundle
      Als Name eignet sich --bundle“ oder /-bundle“.
    7. Verschiebe den Code des LibraryBundles in das neue Repository
      Verschiebe alle Dateien und Ordner aus dem LibraryBundlein das neue Repository. 

                  mv src/Mein/LibraryBundle/* /pfad/zum/neues/repository/
    8. Composer-Support für das LibraryBundle
      Zusätzlich muss das neue Repository noch eine eigene composer.json (und am besten eine README.md) bekommen: 

                  {
                      "name": "paul/library-bundle",
                      "description": "Das ist mein neues LibraryBundle.",
                      "autoload": {
                          "psr-0": { "Paul\\LibraryBundle": "" }
                      },
                      "target-dir": "Paul/LibraryBundle",
                      "require": {
                          "php": ">=5.3.3",
                          "symfony/symfony": "2.1.*"
                      }
                  }

      Die Einträge unter autoload und target-dir sorgen dafür, dass die Klassen im neuen Bundle zuerst an die richtige Stelle im Verzeichnis vendor installiert werden und dann richtig am Autoloader registriert werden.

      Damit ist das neue Repository erst einmal fertig. Im Projekt-Repository kann man jetzt alle Überreste des LibraryBundles entfernen.

                  rm -fr src/Mein/LibraryBundle
    9. Veröffentlichen des LibraryBundles
      Das neue Repository kann jetzt an einem öffentlichen Ort (warum nicht GitHub?) veröffentlicht werden.
       

                  cd /pfad/zum/neuen/repository
                  git add .
                  git commit -m "Initial commit"
                  git add remote origin git@github.com:.git
                  git push origin master

      Wenn das Paket öffentlich sichtbar (nicht nur in der eigenen Organisation) sein darf, kann an dieser Stelle ein Projekt auf Packagist erstellt werden.

 

  • Einbinden des LibraryBundles im Projekt
    Damit das LibraryBundle per Composer installiert werden kann, muss es noch in der composer.jsondes Projekt-Repositorys registrieren.
     

    Wenn das LibraryBundle nicht auf Packagist veröffentlicht wurde, muss ein custom Repository definiert werden:

                "require": {
                    ...
                    "paul/library-bundle": "*",
            		...
                },
            	...
    
                "repositories": [
                    {
                        "type": "git",
                        "url": "git@github.com:Paul/library-bundle.git"
                    },
            		...
                ],
                ...

    Wenn man jetzt im Projekt-Repository noch composer update ausführt, wird das LibraryBundle im Verzeichnis vendor installiert.

 

Herzlichen Glückwunsch!

Deine Library-Funktionalität ist nun vollständig in ein eigenes Bundle und Repository ausgelagert! Dein Projekt sollte genauso funktionsfähig sein wie vor dem Umbau! (Ansonsten solltest du jetzt überlegen, einen Kommentar zu diesem Artikel zu hinterlassen!)

Schlussbemerkungen

Lagere Library-Funktionalität am besten erst aus, wenn sie für den Moment fertig und getestet ist. Wenn der Code noch im Entwicklungs-Stadium ist, solltest du noch abwarten. Das Problem ist, dass Änderungen im Library-Repository committet/gepusht werden müssen und danach das Project-Repository per composer aktualisiert werden muss, nur um eine kleine Änderung im LibraryBundle im Kontext testen zu können.

Wenn du Code im LibraryBundle ändern musst, kannst du die Änderungen direkt auch im Ordner vendor/Mein/LibraryBundle vornehmen. In diesem Ordner liegt ein Clone des Library-Repositorys, in dem du auch committen und pushen kannst! Somit kann man das Problem des vorherigen Abschnitts umgehen. (Über die Sauberkeit dieser Vorgehensweise möchte ich an dieser Stelle jedoch kein Urteil fällen.)

Ein paar Wegweiser zur weiteren Lektüre:

Avatar von Paul Seiffert

Kommentare

4 Antworten zu „Symfony2: Bundle your libraries!“

  1. Lesenswert: Symfony2: Bundle your libraries! http://t.co/6bEfxzkW

  2. please consider writing in english

    nice approach to unfold code to share in bundles

    this is one of the main reasons why the centric one bundle approach was not the default in symfony2 or the decoupled approach having the libraries not being a bundle should also not necessarily be preferred. Bundles are good for code sharing and symfony2 should not be where it is were it not for bundles.

    1. Cordoval,

      I would also appreciate the articles from Mayflower to be in English but I find Google Translate to be effective enough. Mayflower does provide many articles in English as well though.

      On another note, thank you Mayflower for my Zend Framework poster a few years back! It was great at the time. I’m now using Symfony2 though and for good reason. ;-)

      1. Avatar von Paul Seiffert
        Paul Seiffert

        Hi Johnny!

        Thanks for you comment! Have you seen our Symfony2 poster? You can get a digital version of the english poster here: http://www.mayflower.de/de/softwareentwicklung/technologien/symfony-poster-pdf-english

        By now, we are using Symfony2 for many projects, too. Have you tried ZF2?

        Best regards,
        Paul

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.