0251 / 590 837 15
info@a-coding-project.de

RedBeanPHP: Datenbankzugriff auch ohne SQL

Um in PHP seine Daten in eine Datenbank zu speichern, muss man sich als erstes Gedanken über die Datenbank machen und diese zum Beispiel mit der MySQL Workbench erstellen lassen. Für eine gute Performance müssen auch noch Spaltentypen und Indizies passen. Darum müssen wir uns bald nicht mehr kümmern!

Denn RedBeanPHP legt die Datenbankstruktur automatisch zu den Objekten an. Das bedeutet, man muss sich eigentlich gar nicht mehr groß mit der Datenbank auseinandersetzen. Fügt man eine neue Eigenschaft hinzu, legt RedBeanPHP automatisch eine neue Spalte mit passenden Typ an. Und das Beste: Man muss kein neues Datenbanksystem installieren. Es funktioniert zum Beispiel auch direkt mit MySQL.

Installation

Im Download-Bereich von RedBeanPHP kann ein Tar-GZ-Archiv heruntergeladen werden. Dies kann man zum Beispiel mit 7Zip entpacken. Das Archiv enthält die Bibliothek (eine PHP-Datei), die Lizenz und ein Beispiel. Die rb.php kann man nun einfach includieren und benutzen.

Datenbank aufsetzen

Werfen wir doch mal ein Blick in das Beispiel:

<?php

//Example Script, saves Hello World to the database.

//First, we need to include redbean
require_once('rb.php');

//Second, we need to setup the database

//If you work on Mac OSX or Linux (or another UNIX like system)
//You can simply use this line:

R::setup(); //This creates an SQLite database in /tmp
//R::setup('database.txt'); //-- for other systems

//Ready. Now insert a bean!
$bean = R::dispense('leaflet');
$bean->title = 'Hello World';

//Store the bean
$id = R::store($bean);

//Reload the bean
$leaflet = R::load('leaflet',$id);

//Display the title
echo $leaflet->title;

Die Basisklasse heißt einfach nur R. In dem Beispiel wird ein neuer Datensatz (Bean) erstellt, gespeichert und wieder ausgelesen. Das R::setup(); stellt die Datenbankverbindung her. Bei Linux und auf dem Mac dürfte automatisch eine SQLite Datenbank angelegt werden. Ich selbst nutze Windows, daher ist der erste Schritt, eine Datenbankverbindung anzugeben:

<?php
//... gekürzt ...
//Second, we need to setup the database

R::setup('mysql:host=localhost;
          dbname=redbeanphp','root','');

//Ready. Now insert a bean!
$bean = R::dispense('leaflet');
$bean->title = 'Hello World';
//... gekürzt ...

Hier wird einfach ein DSN-String angegeben. Der Server ist bei mir localhost, mein User heißt root und ein Passwort habe ich nicht.

Öffnet man nun die Seite im Browser, wird einem „Hello World“ ausgegeben.

Nun bin ich gespannt, was RedBeanPHP daraus gemacht hat. Einen Blick in die Datenbank zeigt uns, dass folgende Tabelle angelegt wurde:

Neben dem Titel wurde auch eine ID angelegt, um den Datensatz später zu erkennen. Die Namensgebung ist wie in PHP definiert, so kann auch außerhalb von RedBeanPHP leicht darauf zugegriffen werden.

Tabellen verknüpfen

Zunächst einmal legen wir uns ein paar Datensätze an. Wir wollen ein paar Kunden Aufträge zuweisen. Das könnte man so machen:

<?php
require_once('rb.php');
R::setup('mysql:host=localhost;
          dbname=redbeanphp','root','');

$peter = R::dispense('kunden');
$peter->name = 'Peter';
$peter->ownAuftraege = array();

$peter_will_pizza = R::dispense('auftraege');
$peter_will_pizza->anzahl = 10;
$peter_will_pizza->name = "Pizza";
$peter->ownAuftraege[] = $peter_will_pizza;

$peter_will_cola = R::dispense('auftraege');
$peter_will_cola->anzahl = 3;
$peter_will_cola->name ="Cola";
$peter->ownAuftraege[] = $peter_will_cola;

$id = R::store($peter);

Peter hat Hunger und sofort 10 Pizzen und 3 Cola(s?) bestellt. Diese kann man mit ownTabellenname einfach als Array zuweisen und die Verknüpfung wird automatisch gespeichert:

Perfekt, in den Tabellen wurde sogar ein Index angelegt. Wenn man die Seite erneut aufruft, werden die Datensätze erneut eingefügt.

Daten auslesen

Für das Auslesen der Daten habe ich mir ein neues PHP-File erstellt:

<?php
require_once('rb.php');
R::setup('mysql:host=localhost;
          dbname=redbeanphp','root','');

$peter = R::findOne('kunden',
        ' name = ? ',array("Peter"));
		
if(isset($peter))
{
  ?>
    <h1><?= htmlentities($peter->name); ?></h1>
      <ul>
        <?php foreach($peter->ownAuftraege as $auftrag){ ?>
          <li>
            <?= htmlentities($auftrag->anzahl); ?> <?= htmlentities($auftrag->name); ?>
          </li>
        <?php } ?>
      </ul>
  <?php
}
else
{
	echo "<h1>Peter ist weg!</h1>";
}

Hier wird mit FindOne der Datensatz mit dem Namen Peter gesucht. Die Where-Bedingung wird in der SQL-Injection-Sicher-Schreibweise angegeben. Parameter werden automatisch escaped in die Bedingung geschrieben. Neben findOne gibt es auch das normale find, was dann mehrere Datensätze zurückgibt.

Eigene Klassen nutzen

Wer länger mit mir programmiert hat, wird festgestellt haben, dass ich immer sehr objektorientiert arbeite. So möchte ich zum Kunden-Objekt auch eigene Funktionen hinzufügen können, die zum Beispiel Validierungen durchführen.

Auch das ist möglich. Schaut mal:

<?php
require_once('rb.php');
R::setup('mysql:host=localhost;
          dbname=redbeanphp','root','');
class Model_Kunden extends RedBean_SimpleModel
{
  public function name($new_value = null)
  {
    if(is_string($new_value))
    {
        $this->bean->name = $new_value;
    }
    
    return $this->bean->name;
  }
  
  public function update()
  {
    if($this->name() == null || strlen($this->name()) == 0)
    {
      throw new Exception("Name is required");
    }
  }
}

$hans = R::dispense('kunden');
R::store($hans);

Man kann einfach eine Klasse mit Model_Tabellenname erstellen, die die Klasse RedBean_SimpleModel erweitert. Sie wird danach immer genutzt, wenn ein Datensatz geladen wird. Innerhalb der Klasse kann man die Eigenschaften über $this->bean erreichen. Leider habe ich noch nicht herausgefunden, wie man Eigenschaften privat machen kann, so dass sie von außen nicht mehr erreich werden. Wer da etwas kennt, einfach Bescheid geben.

Die Update-Funktion ist ein Hook. Sie wird aufgerufen, bevor der Datensatz gespeichert wird. Somit kann man dort Exceptions werfen, um ein Speichern zu verhindern. Neben update gibt es noch weitere solcher Hooks:

  • after_update wird nach dem Update aufgerufen.
  • open wird direkt nach dem Laden aufgerufen
  • delete wird vor dem Löschen aufgerufen
  • after_delete wird vor dem Löschen aufgerufen
  • dispense wird beim erstellen eines neuenObjekts aufgerufen

Freezing

In der Live-Umgebung sollte man aus Performance- und Sicherheitsgründen das Erweitern der Datenbank verbieten. Dafür muss man einfach nur freeze(true) aufrufen.

Fazit

Redbean finde ich nach den ersten Versuchen richtig gut. Es nimmt uns Entwicklern eine Menge Arbeit ab und entfernt das SQL aus dem Quellcode ohne auf ein komplett neues Datenbanksystem umzusteigen. Neben den hier vorgestellten Funktionen hat es noch ein paar weitere schöne Extras. Unter anderem Caching, Transaktionen und auch die Verwendung mehrere Datenbanken.

Kommentare

Lukas schrieb am 12.04.2013:

Danke für das ausführliche Tutorial. Für das nächste Projekt werde ich mir RedBeanPHP auf jedenfall vormerken.