Gastbeitrag: PHP AutoMapper

Bei der Entwicklung einer MVC-Web-Anwendung gelangen Sie normalerweise zu dem Punkt an dem Sie zwei mehr oder weniger äquivalente Modelle haben. Auf der einen Seite haben Sie ein View-Modell - ein in der Regel gut strukturiertes Objekt um Daten auf Ihrer Webseite anzuzeigen. Auf der anderen Seite haben Sie ein Modell aus Ihrer Datenbank oder einem Repository das die Daten enthält, wie sie in der Datenbank vorliegen. Diese beiden Modelle sind nicht identisch, aber üblicherweise sehr ähnlich.

Ein Beispiel könnte ein einfacher Blog-Eintrag sein: Während Titel, Autor und Text Ihres Posts in beiden Modellen gleich sind, unterscheiden sich z.B. das formatierte Veröffentlichungsdatum vom UNIX Timestamp oder DateTime-Objekt, welches aus der Datenbank kommt. Vielleicht möchten Sie auch eine kommaseparierte Liste aus Tags anzeigen, während die Datenbank ein Array aus Strings zurückgibt. Im Allgemeinen müssen Sie viel Zeit investieren und Boilerplate-Code schreiben, um ein Objekt auf ein anderes zu mappen, obwohl viele - wenn nicht gar die meisten - Eigenschaften nicht nur identisch sind, sondern sogar den gleichen Namen besitzen. So schreiben Sie am Ende Code wie:

$blogpostModel->Title = $blogpostDTO->Title;

Viele MVC-Frameworks umgehen dieses Problem, indem sie ein shared Modell für die Datenbank und die MVC Applikation verwenden. Dies ist allerdings ein eher unsauberer Ansatz, da man dadurch ungewollte Abhängigkeiten zwischen zwei Schichten bekommt, welche aus offensichtlichen Gründen getrennt sein sollten. Erstens könnte es zum Beispiel sein dass Sie Ihre Datenbank-Persistenzschicht ersetzen wollen, weil sich das Design - oder auch nur Spalten- oder Tabellennamen - geändert hat. Zweitens ist es unvorteilhaft, wenn die Persistenzschicht abhängig von der MVC-Applikation ist, denn dies erfordert bei jeder änderung des View-Models - beispielsweise bei einem Redesign - Anpassungen an der Persistenzschicht. Dies stellt auch ein echtes Problem dar wenn die selbe Persistenzschicht auch in anderen Applikationen Verwendung findet, zum Beispiel in einem CMS, welches ebenfalls Posts anzeigen kann, aber eventuell mit einem anderen View-Modell als Ihre Webseite.

Wenn Sie also beide Schichten unabhängig pflegen und auf einer sauberen Architektur aufbauen wollen, werden Sie am Ende Klassen mit fast identischen Eigenschaften mappen. In der .NET Welt existiert eine nette kleine Bibliothek namens AutoMapper. Diese ermöglicht es zwei Objekte aufeinander zu mappen, anhand von Konventionen und Mapping-Rules, sowie Typen-Konvertern und Value-Resolvern. Leider gibt es keine derartige Bibliothek für PHP - aus einem guten Grund: PHP ist nicht typsicher! Während Type-Hinting für Funktionsparameter mittlerweile unterstützt wird (zumindest für Arrays, Klassen und Interfaces), kann der Typ eines Rückgabewerts leider immer noch nicht definiert werden. Wie sollte also ein PHP basierter AutoMapper den korrekten Datentyp eines Propertys bestimmen, um ein sicheres Mapping zu garantieren? Es wäre möglich die Source-Klasse durch Reflection zu analysieren, und die Datentypen der Propertys auszulesen - aber was wäre mit der Ziel-Klasse? Diese ist nicht instanziiert und besitzt keine Werte. Eine eher hässliche Möglichkeit wäre es, das Ziel-Objekt mit Default-Werten zu initialisieren, um danach die Datentypen zu analysieren. Allerdings ist dies wohl eher als Hack anzusehen.

Es gibt allerdings eine Lösung, dieses Problem sauber zu umschiffen: Man interpretiert die PHPDoc Elemente für jede Klassen-Eigenschaft. Wenn Sie es ohnehin gewohnt sind, Ihre Klassen zu dokumentieren, dann ist dies eine Konvention, die man leicht ohne Mehraufwand erfüllen kann. Falls nicht ist es dennoch kein Problem jede Eigenschaft mit einer Zeile phpDoc-Code wie /** @var DateTime */ oder /** @var CommentModel */ zu versehen, um dem PHP basierten AutoMapper die Analyse des benötigten Datentyps zu ermöglichen. Ziemlich einfach, oder?

Die Open-Source-Lösung PHP AutoMapper erledigt diesen Job. Sie kann Ihre Modelle automatisch mappen, wobei Eigenschaften gleichen Namens und gleichen Typs kopiert werden. Sie können auch Mapping Rules definieren, falls Sie Eigenschaften unterschiedlicher Namensgebung oder unterschiedlicher Typen mappen möchten. Und natürlich ist es möglich, auch eigene Typenkonverter zu erstellen. Selbst das Mappen mehrerer Source-Propertys auf ein Target-Property stellt dank selbst erstellbarer Value-Resolver kein Problem dar.

//create a mapper instance
$mapper = new \Adminomatic\AutoMapper\Mapper();
//map their property to mine
$mapper->CreateMap('DestinationClass::MyProperty', 'SourceClass::DifferentlyNamedProperty');
//map their property to mine using the given type converter
$mapper->CreateMapUsingConverter('DestinationClass::MyConvertedProperty', 'SourceClass::DifferentlyNamedProperty', new \Adminomatic\AutoMapper\ImplodeConverter());
//map multiple source properties to my single property
$mapper->CreateMapUsingResolver('DestinationClass::MyCombinedProperty', new \CustomResolver());
//map my source object to my destination class
$myDestinationObject = $mapper->Map(new \DestinationClass(), $sourceObject);

Weitere Anwendungsbeispiele, die Interface-Dokumentation, Konventionen sowie den freien Source-Code (MIT Lizenz) finden Sie unter http://automapper.nylle.de. Soweit ich weiss ist dies die einzige verfügbare Lösung für PHP. Probieren Sie es aus wenn Sie es leid sind, das Mapping von Objekten selbst vorzunehmen.

Kommentare (0)

Wenn Sie möchten können Sie einen Kommentar hinterlassen!
Felder mit einem Stern (*) sind Pflichtfelder.

wird nicht veröffentlicht
Durch das Anhaken dieser Checkbox erklären Sie sich mit der Speicherung Ihrer Daten durch diese Webseite einverstanden. Um Kommentare anzeigen zu können speichert diese Webseite den angegebenen Namen, die E-Mail Adresse, die angegebene Webseite, den Kommentartext sowie den Zeitstempel. Auf Wunsch können Sie Ihre Kommentare später wieder löschen lassen. Detaillierte Informationen finden Sie in meiner Datenschutz-Erklärung.