Was’n das?

Finger weg! Diese Mango schmeckt nicht.

Mango ist ein Object Document Mapper f├╝r MongoDB und PHP.

Und MongoAppKit?

MongoAppKit hat ein Problem, denn es ist nicht nur ein ODM und kann noch so einiges mehr. Theoretisch kann man zwar die ODM-Komponente auch ohne den ganzen anderen Krempel nutzen, aber es bleibt eine gro├če Abh├Ąngigkeit zu Silex, die man auch nicht so einfach wieder los wird.

Dies und das im Vergleich schlechte Handling bzw. der geringe Komfort von MongoAppKit, haben mich dazu bewogen mit Mango einen universell einsatzbaren und leicht handzuhabenden ODM zu entwickeln.

Mango wurde stark von Mongoid f├╝r Ruby inspiriert und soll dessen Funktionalit├Ąt zumindest teilweise in PHP abbilden. Das ist einfacher gesagt als getan, denn Ruby bietet wesentlich elegantere M├Âglichkeiten diverse Probleme zu l├Âsen, als es mit PHP derzeit machbar ist.

Los geht’s ÔÇŽ

Installation via Composer

$ php composer.phar require webcodr/mango:*

Ein Dokument anzulegen ist ein Kinderspiel

<?php

namespace MyProject\Model;

use Mango\Document;
use Mango\DocumentInterface;

class User implements DocumentInterface
{
    use Document;

    private function addFields()
    {
        $this->addField('name', ['type' => 'String']);
        $this->addField('email', ['type' => 'String']);
        $this->addField('created_at', ['type' => 'DateTime', 'index' => true, 'default' => 'now'];
        $this->addField('updated_at', ['type' => 'DateTime', 'index' => true, 'default' => 'now'];
    }
}

Es ist lediglich n├Âtig, dass die Model-Klasse das Interface DocumentInterface implementiert und den Trait Document einbindet. In der Hook-Methode addFields() werden anschlie├čend noch die Felder des Dokuments deklariert.

Mango nutzt etwas Magic: Der Klassenname des Models ist gleichzeitig auch der Name der Collection (klein geschrieben). Soll die Collection anders hei├čen bzw. das Model eine vorhandene nutzen, muss lediglich die Methode getCollectionName() ├╝berschrieben werden.

Go, Mango, go!

<?php

use Mango\Mango;
use Mango\DocumentManager;

use Document\User;

$mango = new Mango('mongodb://localhost/galactica');
$dm = new DocumentManager($mango);
$user = new User();
$user->name = 'William Adama';
$user->email '[email protected]';
$user->store();

Das Mango-Object erwartet eine g├╝ltige MongoDB URI, falls notwendig inkl. Benutzer, Passwort, Port usw.

Dem Document Manager kommt eine vergleichbare Aufgabe zu, wie dem Entity Manager in Doctrine2.

F├╝r mehr Komfort holt sich eine Model-Klasse den Document Manager ├╝ber eine statische Methode ab. Daher k├Ânnen Methoden wie store() direkt ├╝ber die Model-Klasse abgewickelt werden.

Dokumente abfragen

<?php

$user = User::where(['name' => 'William Adama']);
echo $user->count(); // = 1
echo $user->first()->email; // = [email protected]

Eine Abfrage kann einfach ├╝ber die statische Methode where() ausgef├╝hrt werden. Die Syntax der Abfragen entspricht derzeit noch der normalen MongoDB Query API. F├╝r die Zukunft plane ich aber eine Abstraktionsebene f├╝r die Abfragen, vergleichbar mit Mongoid.

Eine Abfrage kit where() oder find() gibt immer ein Cursor-Objekt zur├╝ck, das anhand der aufgerufenen Methode entscheiden kann, ob der Zugriff auf den MongoCursor oder das Abfrageergebnis in Form einer Instanz von MutableMap erfolgt.

In obigem Beispiel ist count() eine Methode des Cursors, w├Ąhrend first() schon auf der Ergebnis zugreift. Wie MongoCursor kann auch die Cursor-Klasse von Mango einfach ├╝ber das Ergebnis iterieren.

Durch die dynamische Unterscheidung zwischen MongoCursor- und Datenzugriff, k├Ânnen auf eine Instanz der Cursor-Klasse auch alle Methoden von MutableMap angewandt werden.

Beispielsweise:

<?php

User::where()->reverse()->slice(0, 2)->each(function($document) {
    echo $document->name;
});

Nat├╝rlich macht dieser Code wenig Sinn, da man das wesentlich effizienter ├╝ber die Cursor-Methoden erledigen kann. Das Beispiel soll lediglich zeigen, was m├Âglich w├Ąre.

Hydration

Mango sorgt automatisch daf├╝r, dass die Dokumente im Ergebnis immer Instanzen ihrer jeweiligen Model-Klasse sind.

Die Hydration-Automatik sorgt au├čerdem daf├╝r, dass die Daten intern als jeweilige Typ-Klasse von Mango gehalten werden.

Typ-Klassen halten die Daten und k├Ânnen sie in zwei Formaten zur├╝ckgeben. Konfiguriert man ein Feld als DateTime bekommt Mango intern beim Speichern automatisch ein MongoDate-Objekt. Greift man hingegen au├čerhalb von Mango auf den Wert zu, bek├Ąme man in diesem Fall eine Instanz der Klasse DateTime zur├╝ck.

Soweit zum aktuellen Funktionsumfang von Mango. Es ist bei weitem noch nicht fertig, kann aber f├╝r kleine Projekte schon einsetzt werden. Ich verwende es selbst in der aktuellsten Version von CodrPress und es macht wesentlich mehr Spa├č als MongoAppKit, ohne ein monstr├Âses Schlachtschiff wie Doctrine zu sein.

Nat├╝rlich gibt’s Mango auch bei GitHub.