Drupal auf composer-Projekt umstellen

8
min read
A- A+
read
drupal 8 composer projekt

ComposerCat

Eine bestehende Drupal-8 Website auf die aktuelle Version des Drupal-Composer Projekts "mergen", um diese Website in Zukunft mit Composer upzudaten. Unterwegs werden auch ein paar Änderungen eingeführt, die das Umstellen der Umgebungen (Lokaler Webserver und Entfernter Webserver beim Hoster) vereinfachen.

Eine funktionierende lokale Webserver-Umgebung wird vorausgesetzt: LAMP, WAMP, Virtualbox oder ähnliches. Hier gibt es zahlreiche Lösungen. Ich persönlich habe mir auf meinem Mac parallel zum "eingebauten Webserver", dessen Konfiguration durch System-Updates verändert wird, mit homebrew eine Umgebung aufgebaut.

Für die lokale Ablage der Websites nutze ich ein Synology NAS mit genügend Terabytes Plattenspeicher für sehr viele Websites. Die Variante, eine lokale Webserver-Umgebung direkt auf dem NAS zu etablieren, habe ich getestet und verworfen.

Die Variante mit homebrew ist flexibler und kann jederzeit an Erfordernisse angepasst werden.

Vorteile von Composer

Composer ist eine Art "Paketmanager" für PHP-basierte Projekte, wie z.B. Drupal und viele andere. Diese nutzen Bestandteile anderer Projekte, um best. Funktionen bereitzustellen. So müssen die Programmierer nicht immer das Rad neu erfinden und es können sich Standards entwickeln.

Anstatt wie früher Drupal von drupal.org als "Tarball" (gepacktes Archiv) runterzuladen, erzeugt man einfach ein "Drupal-Projekt". Wenn eine Website erstmal auf den Stand des aktuellen Drupal-Composer Projekts gebracht ist, ergeben sich hier vor allem Geschwindigkeitsunterschiede beim Updaten auf neue Drupal-Versionen.

Auch das Installieren / Entfernen von Modulen, JavaScript Libraries etc. ist in Zukunft ein einfacher Befehl auf Kommandozeile oder ein paar Mausklicks in ComposerCat.

Composer lebt hier: https://getcomposer.org/

Normalerweise wird Composer via Shell (Kommandozeile) bedient, es gibt aber auch ein nettes Tool namens ComposerCat, mit dem man eine grafische Oberfläche hat. Diese lebt hier: https://www.getcomposercat.com/

Begrifflichkeiten

Website-Dateien
Alle Dateien, die zu einer Website gehören. Einfach alles.
Drupal-Dateien
Alle Dateien, die zu Drupal selbst gehören und z.B. durch Installer oder Composer erzeugt werden.
User-Dateien
Alle Dateien, die User zum Inhalt der Website hinzugefügt haben, z.B. Bilder, Dokumente etc. Normalerweise liegen diese unter /sites/default/files.
Drupal-DB
Die verwendete Drupal-Datenbank. Ich gehe hier von einer einfachen Installation mit 1 Datenbank für 1 Website aus. Bei Verwendung von mehreren Datenbanken entsprechend verfahren.
Projekt-Name = Projekt-Ordner = Projekt
Vorgeschlagen wird, den Domain-Namen ohne .tld (Top-Level Domain) als Projekt-Namen / Ordner-Namen zu verwenden, für eine unfallfreie Zuordnung zwischen Projekt und Website. Aus [meine-domain.de] wird also das Projekt [meine-domain].

Unterschied Drupal Tarball zu Composer-Projekt

Diese Tabelle zeigt grob die wichtigsten Unterschiede zwischen Tarball und Composer-Projekt. Zu beiden gehören noch weitere Dateien, hier sind nur die wichtigsten aufgeführt.

"Tarball"
Drupal runterladen und entpacken
Composer
Mit Composer erzeugte Sttuktur

{Projekt-Ordner} = WebRoot

  • .htaccess
  • autoload.php
  • composer.json
  • composer.lock
  • core/ -- Drupal selbst
  • index.php
  • modules/
    • contrib/
    • custom/
  • profiles/
  • robots.txt
  • sites/
    • default
      • files
      • settings.php
  • themes/
    • contrib/
    • custom/
  • update.php
  • vendor/

{Projekt-Ordner}

  • .env.example
  • composer.json
  • composer.lock
  • drush/
  • load.environment.php
  • scripts/
  • vendor/
  • web/ -- WebRoot
    • .htaccess
    • autoload.php
    • core/ -- Drupal selbst
    • index.php
    • libraries/ -- JavaScript
    • modules/
      • contrib/
      • custom/
    • profiles/
      • contrib/
      • custom/
    • robots.txt
    • sites/
      • default/
        • files/
        • settings.php
    • themes/
      • contrib/
      • custom/
    • update.php

Nach diesen Vorab-Klärungen nun direkt zum Workflow.

Auf dem Webserver

Diese Vorgänge werden auf dem Webserver durchgeführt.

Vorgang Beschreibung Zeit-Schätzung
Website-Dateien Backup

Alle Dateien, die zur Website gehören, sichern. Über ein Tool des Hosters oder manuell in der Shell mit gzip oder ähnlichem.

Bei Hosteurope z.B. einfach mit "Backup on the fly" - auch zusammen mit Drupal-DB in einem Rutsch.

15 min.
Drupal-DB Backup Die für die Website verwendete Datenbank sichern. Mit Tool des Hosters, phpMyAdmin oder ähnlichem. 5 min.

Die gesicherten Dateien mit den Backups der Dateibasis und der Datenbank lokal runterladen, z.B. per FTP.

Lokal 

Diese Vorgänge werden lokal durchgeführt.

Vorgang Beschreibung Zeit-Schätzung
Projekt-Ordner anlegen Ordner erzeugen: {Projekt-Ordner} 1 min.
 

Hierin weitere Unter-Ordner erzeugen, die wir gleich brauchen werden:

  • assets/
    • backup/
    • db-dump/
    • favicons/
  • config/
    • sync/
  • private/

 

1 min.
 

Das {Website-Dateien Backup} in [assets/backup/] und das {Drupal-DB Backup} in [assets/db-dump/] ablegen.

{Website-Dateien Backup} auspacken.

{Drupal-DB Backup} in lokales SQL-Tool importieren. Name der DB gleich {Projekt-Name}

10 min.
Composer-Projekt erzeugen

In einer Shell (Kommandozeile) mit folgendem Befehl ein aktuelles Drupal-Composer Projekt im {Projekt-Ordner} anlegen:

cd {Projekt-Ordner}

composer create-project drupal-composer/drupal-project:8.x-dev my_site_name_dir --no-interaction --no-dev

Im Detail:

composer   -- der Paketmanager composer(.phar)
  create-project -- leg Drupal-Projekt an
    drupal-composer/drupal-project:8.x-dev -- Mindest-Stabilität: dev
    my_site_name_dir -- tmp. Ordner, wird nicht lange gebraucht
    --no-interaction   -- automatischer Lauf
    --no-dev   -- pot. unsichere Entwicklungsdinge nicht mit installieren

--no-dev: Wer keinen eigenen Sourcecode für Drupal, z.B. in Form von Modulen, erzeugt, benötigt die Development-Dinge nicht. Falls doch, einfach --no-dev weglassen. Die "dev" Pakete sollten, wenn überhaupt, nur lokal, nicht aber auf dem Produktions-/Live-Server verwendet werden.

15 min.
Manuelles "mergen" Den Inhalt von [my_site_name_dir] eine Ordner-Ebene höher ziehen (inklusive der "unsichtbaren" Punkt-Dateien), damit die von Composer erzeugten Dinge direkt unter {Projekt-Ordner} liegen. 1 min.
  Der jetzt leere Ordner [my_site_name_dir] kann gelöscht werden. 1 min.
 

2 Datei-Browser-Fenster öffnen:

Ansicht 1: {Projekt-Ordner}/assets/backup/*

Ansicht 2: {Projekt-Ordner}

Im Folgenden meint {Backup} die Ansicht auf {Projekt-Ordner}/assets/backup/*

1 min.
  Alle Dinge aus dem {Backup} an ihre jeweilige, korrekte Position in der Drupal Dateistruktur schieben. Siehe unten "Drupal Dateistruktur". 30 min.
composer.json

Alle verwendeten Module in composer.json eintragen.

Folgende Varianten sind möglich:

  • Texteditor
  • Shell mit composer require drupal/namedesmoduls:version
  • ComposerCat

 

30 min.
composer update

Mit dem Ausführen von composer update wird alles auf den aktuellen Stand gebracht. Drupal selbst, alle verwendeten (und in composer.json eingetragenen) Module sowie Abhängigkeiten im vendor/ Verzeichnis uvm.

Sollte das updaten Probleme bereiten, einfach den Ordner vendor/ löschen. Dieser wird automatisch neu erzeugt und bestückt.

15 min.
Settings

Die Datei {Projekt-Ordner}/.env mit Informationen aus den Settings-Dateien des {Backup} füllen.

{Projekt-Ordner}/sites/default/settings/* überprüfen, ggf. nach Wunsch ändern. Siehe "Drupal Settings" Abschnitt unten.

5 min.
Lokal Web Anschließend die Website lokal aufrufen, update.php ausführen und ggf. Fehlermeldungen fixen. 10 min.
Trusted Host Es wird empfohlen, die Einstellungen für Trusted Hosts in /sites/default/settings/settings.shared.php vorzunehmen. 5 min.

Fertig. Die vorherige Website ist jetzt auf das aktuelle Drupal-8-composer Projekt umgestellt und kann hochgeladen werden. Von der lokalen (jetzt aktuellen) Datenbank einen Export ziehen und auf dem Webserver entsprechend importieren.

Auf dem Webserver muss nach dem Hochladen der Dateien und der Datenbank nur in der Datei .env der Schalter von "local" auf "prod" umgestellt werden.

Drupal-Dateistruktur

  • {Projekt-Ordner}/
    • .editorconfig
    • .env -- hier stehen geheime Dinge wie DB-Zugang etc.
    • .env.example
    • .gitattributes
    • .gitignore -- Wichtige Datei, was soll nicht durch git verwaltet werden
    • .travis.yml
    • assets/
      • backup/
      • db-dump/
    • composer.json
    • composer.lock
    • config/
      • sync/
    • drush/ -- (Drush-Zeugs)
    • LICENCE
    • load.environment.php
    • phpunit.xml.dist
    • private/ -- (für “private” Dateien, die nicht übers Web erreichbar sein sollen)
    • README.md
    • scripts/
    • composer/ -- (composer-Zeugs)
    • vendor/ -- (alle PHP-Projekte, die Drupal-8 bezieht)
    • web/ ← auf diesen Ordner soll später die Domain zeigen
      • core/ -- (Drupal-8 mit all dem, was es ausmacht)
      • index.php -- Diese wird vom Webserver aufgerufen und ist der Startpunkt der Website (nicht bearbeiten)
      • libraries/ -- (Verzeichnis für JavaScript-Dinge, die evt. von Modulen benötigt werden)
      • modules/
        • custom/ -- Module, die selbst gebaut oder gekauft sind
        • contrib/ -- Module von drupal.org
      • profiles/
        • custom/
        • contrib/
      • robots.txt
      • sites/
        • default/
          • default.services.yml
          • default.settings.php
          • files/ -- (gecachte Drupal Dateien und {User-Dateien}
            • php/ -- twig-Cache Dateien, werden autom. erzeugt
          • services.yml -- Durch Duplizieren von [default.services.yml] erzeugt
          • settings/
            • settings.local.php
            • settings.prod.php
            • settings.shared.php
          • settings.php -- Includiert [settings/settings.shared.php]
        • development.services.yml
        • example.settings.local.php
        • example.sites.php
      • themes/
        • custom/ -- Themes, die selbst gebaut oder gekauft sind
        • contrib/ -- Themes von drupal.org
      • update.php
      • web.config -- Datei für IIS Webserver

Drupal Settings

Anstatt die Datei /sites/default/settings.php jedesmal anzupassen, wenn die Webite zwischen “lokal” und “Server” verschoben wird, und um mehr Sicherheit für die verwendeten Datenbank-Zugänge etc. zu bieten, werden diese sensiblen Daten jetzt in der Datei .env festgehalten, die sich außerhalb des Webroots (/web) befindet, so dass sie für den Webbrowser nie erreichbar ist.

Als 2. Verbesserung wurde die Struktur für die Settings überdacht und Folgendes ist entstanden:

/sites/default/settings.php: enthält nur den Aufruf der Datei /sites/default/settings/settings.shared.php

Je nachdem, was in den Wert von APP_ENV in der .env geschrieben wird {local, prod} werden entweder /sites/default/settings/settings.local.php ODER /sites/default/settings/settings.prod.php nachgeladen.

Hiermit ist nun eine saubere Trennung zwischen Umgebungen (Environments) möglich.

Diese Idee habe ich bei blog.netgloo.com aufgeschnappt und fand es prima.

Die Struktur ist jederzeit erweiterbar. So könnten auch spezielle Werte für das Hosting auf anderen Server-Locations wie z.B. Pantheon hier in einer eigenen “Untersettingsdatei” festgehalten und durch einen entsprechenden Wert in APP_ENV angesprochen werden.

Da ich den Tmp-Pfad für die jeweilige Umgebung in der jeweiligen Settings-Datei vorgebe, lösche ich in der Website im Backend den Wert unter /admin/config/media/file-system. Das ist problemlos möglich. Es gibt sogar Module für die Anzeige im Backend, welche dort eingetragenen Werte von Werten aus Settings-Dateien überschrieben werden.

Datei /sites/default/settings.php:

<?php
include __DIR__ . '/settings/settings.shared.php';

Datei /sites/default/settings/settings.shared.php

Diese Datei wird durch Duplizieren der default.settings.php Datei erzeugt. Hiier werden nur die wichtigen Stellen gezeigt, diese Datei ist sehr lang und bietet eine Unmenge an Konfigurationsmöglichkeiten:

<?php
// @codingStandardsIgnoreFile

# get environment from .en file at project root
$env = getenv('APP_ENV');

/* force https
 * uncomment if site runs on ssl
 */
#if ( (!array_key_exists('HTTPS', $_SERVER)) && (PHP_SAPI !== 'cli') ) {
#  $new_url = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
#  header('HTTP/1.1 301 Moved Permanently');
#  header('Location: https://'. $new_url);
#  exit();
#}
...

$settings['hash_salt'] = getenv( 'hash_salt' );
...

 $settings['trusted_host_patterns'] = array(
   '^localhost$',
   '^127.0.0.1$',
   '^meine-domain\.sites\.test$',
   '^meine-domain\.de$',
   '^www\.meine-domain\.de$',
   '^.+\.meine-domain\.de$'
 );
...

# yaml config dir
$config_directories['sync'] = '../config/sync';

# private file path.
if (!isset($settings['file_private_path'])) {
  $settings['file_private_path'] = '../private';
}

# get settings based on application environment (.env file)
$base_path = $app_root . '/' . $site_path;
$settingsfile = $base_path . '/settings/settings.' . $env . '.php';

if( file_exists($settingsfile)) {
  include $settingsfile;
}

Datei /sites/default/settings/settings.local.php:

# get temporary file path from .env
#if (!isset($config['system.file']['path']['temporary'])) {
$config['system.file']['path']['temporary'] = getenv('local_tmp_dir');
#}

# db settings.
$databases['default']['default'] = array (
  'database' => getenv('local_DB_DATABASE'),
  'username' => getenv('local_DB_USER'),
  'password' => getenv('local_DB_PASS'),
  'prefix' => getenv('local_DB_PREFIX'),
  'host' => getenv('local_DB_HOST'),
  'port' => getenv('local_DB_PORT'),
  'namespace' => getenv('local_DB_NAMESPACE'),
  'driver' => getenv('local_DB_DRIVER'),
);

$config['system.site']['name'] = 'Local DEV';

Datei /sites/default/settings/settings.prod.php:

# get temporary file path from .env
#if (!isset($config['system.file']['path']['temporary'])) {
$config['system.file']['path']['temporary'] = getenv('prod_tmp_dir');
#}

# db settings.
$databases['default']['default'] = array (
  'database' => getenv('prod_DB_DATABASE'),
  'username' => getenv('prod_DB_USER'),
  'password' => getenv('prod_DB_PASS'),
  'prefix' => getenv('prod_DB_PREFIX'),
  'host' => getenv('prod_DB_HOST'),
  'port' => getenv('prod_DB_PORT'),
  'namespace' => getenv('prod_DB_NAMESPACE'),
  'driver' => getenv('prod_DB_DRIVER'),
);

.env-Datei

Hier sollen die sensiblen Daten eingetragen werden, die sonst in /sites/default/settings.php festgehalten werden.

Hier ist ein Beispiel für die .env-Datei, die ich nutze:

# PHOENIXSEO .env
# Environment Settings local and production
#

# switch environment: 'local' or 'prod'
APP_ENV='local'

# hash salt value
hash_salt='hash-salt-wert-aus-settings.php'

# local temp-dir
local_tmp_dir='/mnt/webroot/webtmp'

# prod temp-dir
prod_tmp_dir='../../tmp'

# local environment database credentials
local_DB_HOST='localhost'
local_DB_DATABASE='{Datenbank-Name}'
local_DB_USER='{Datenbank-Benutzer}'
local_DB_PASS='{Datenbank-Passwort}'
local_DB_PREFIX=''
local_DB_PORT='3306'
local_DB_NAMESPACE='Drupal\\Core\\Database\\Driver\\mysql'
local_DB_DRIVER='mysql'

# production environment database credentials
prod_DB_HOST='localhost'
prod_DB_DATABASE=’{Datenbank-Name-auf-Webserver}’
prod_DB_USER='{Datenbank-Benutzer-auf-Webserver}'
prod_DB_PASS='{Datenbank-Passwort-auf-Webserver}'
prod_DB_PREFIX=''
prod_DB_PORT='3306'
prod_DB_NAMESPACE='Drupal\\Core\\Database\\Driver\\mysql'
prod_DB_DRIVER='mysql'
#

Drupal Multisite

Eine Drupal Multisite Installation (1 x Drupal-Installation + mehrere Datenbanken, Domains, Websites) lässt sich jederzeit in eine Drupal Einzel-Installation überführen und umgekehrt.

Hierzu müssen nur die jeweiligen Dinge wie das Theme, die genutzten Module und die Dateien sowie Settings an die korrekten Ablage-Orte geschoben bzw. kopiert werden. Anschließend soll ein /update.php alles auf Stand bringen.

Besonderes Augenmerk sollte hierbei auf den Ordner mit den {User-Dateien} gelegt werden. In einer Einzel-Installation liegt dieser unter
/sites/default/files.

Bei einer Multisite-Installation unter
/sites/{meine-domain.tld}/files.

Wenn im Inhalt der Website “hart” auf Bilder verlinkt wird, stimmt der Pfad nach einer Transformation nicht mehr. Um den Pfad /sites/{meine-domain.tld}/files/{irgendein-ordner}/mein-bild.jpg beizubehalten, könnte man auch in einer Einzel-Installation einfach diese Struktur beibehalten.

Falls der Webserver den files-Ordner anschließend nicht findet, kann man die Datei sites.php zu Hilfe nehmen und dort manuell eine Zuordnung eintragen.

Service