Database

Aufbau

Das SetaFramework beinhaltet die Database-Klasse um mit einer Datenbank zu kommunizieren. Die Database-Klasse ist grundsätzlich ein Wrapper von PDO. Aber anstatt lediglich einen anderen internen Treiber zu verwenden, wie bei PDO sind die unterstützten Plattformen als einzelne Klassen implementiert, welche die Database-Klasse erweitern:

  • Mysql - beste Unterstützung
  • Sqlite (keine Unterstützung für den Database Manager)
  • Sqlsrv (keine Unterstützung für den Database Manager)

Wichtig sind vor allem folgende Methoden:

executeQuery()

Executes the query.

factory()

Factory method to create an instance of the database.

queryAll()

Returns an associative array with all data received by the query

queryAllAsGenerator()

queryColumn()

Returns a specific column by the given query

queryOne()

Returns the first column in the first row received by the query

queryRow()

Returns the first received row by the query

DatabaseConnectionManager

Vor SF v5.0 musste jede Applikation sich selbstständig darum kümmern eine Verbindung zur Datenbank aufzubauen. Da aber im gleichen Zuge fast alle Applikationen sich mit der selben Datenbank verbinden mussten, entstand hierbei eine Kopplung der Applikationen unter einander. Beispiel: Konquadrat und andere Applikationen besaßen ein "useSetasite"-Flag bei der Datenbank-Konfiguration.

Aus diesem Grund gibt es nun einen globalen DatabaseConnectionManager bei dem die entsprechenden Applikationen ihre Datenbank-Verbindung anfragen können.

Der DatabaseConnectionManager besitzt eine eigene Konfiguration und kann mehrere Datenbanken konfiguriert haben und über einen eindeutigen Identifier können die Datenbank-Verbindungen aufgerufen werden.

; bootstrap.ini
database = @@db.ini

; ------------------------------
; db.ini
default.driver = mysql
default.dsn = "$MYSQL_DSN$"
default.username = "$MYSQL_USER$"
default.password = "$MYSQL_PASSWORD$"
default.logger.enabled = 1
default.logger.handler = additionalBlubLogger

secondConnection.driver = sqlite
secondConnection.dsn = sqlite::memory:

; ------------------------------
; application.ini - neue Variante über den ConnectionManager
db = secondConnection ; optional, falls nicht hinterlegt wird "default" verwendet

; ------------------------------
; application.ini - alte Fallback Variante, die am ConnectionManager vorbei arbeitet
db.driver = mysql
db.dsn = "$MYSQL_DSN$"
db.username = "$MYSQL_USER$"
db.password = "$MYSQL_PASSWORD$"
PHP
// Konfiguration vom DI

$di = $this->getDi();
$di->addAlias(
    Database::class,
    static function (DatabaseConnectionManager $connectionManager, ApplicationConfig $config) {
        return $connectionManager->getConnectionByApplicationConfig($config);
    }
);

QueryBuilder

Das SetaFramework beinhaltet einen QueryBuilder, der es ermöglicht objektorientiert Queries zu schreiben. Zusätzlich nutzen wir diesen ebenfalls um möglichst elegant SQL-Injections zu verhindern, da der QueryBuilder selbstständig quotet und wir somit nicht auf preparedStatements angewiesen sind. Die Syntax der Klassen ist dabei so nah wie möglich am echten SQL Query gehalten.

Beispiel:

PHP
<?php

$query = (
    Database\Query::select([
        ['users.id', 'id'],
        ['users.name', 'name'],
        ['permissions.flags', 'permissions']
    ])
    ->from('users')
    ->joinInner('permissions')->on('users.id', '=', 'permissions.userId')
    ->where('age', '>', 10)
);
/**
 * @var Database $database
 */
$database->queryAll($query);

Quoten im QueryBuilder

Die Parameter können unterschiedlich gequotet werden. PDO kann für üblich eigentlich nur Werte quoten und keine Identifier (ein Identifier kann z.B. ein Spaltenname oder ein Tabellenname sein). Die einzelnen Methoden quoten bestimmte Parameter anders als Andere und das Standardverhalten ist nicht immer das gewünschte. An den einzelnen Methoden steht immer dran als was der Parameter gequotet wird: Wert oder Identifier. 

Um ein bestimmtes Verhalten zu erzwingen kann man den Parameter in einer ValueExpression oder einem Identifier kapseln. 

Um das quoten komplett zu verhindern, weil man z.B. eine Mysql-Methode verwenden möchte, muss man den entsprechenden Text in einer Expression kapsel. (z.B. ->where('creation_date', '>', new Expression('now()')))
Hierbei sollte umbedingt beachtet werden, dass man hier durch keine SQL-Injections ermöglicht. 

Ansonsten gibt es noch eine besondere Klasse zum quoten: QuoteLaterExpression
Die QuoteLaterExpression erwartet eine Closure als Parameter. Sobald die Expression gequotet wird, wird die Closure aufgerufen mit einem QuoteInterface. Die Closure MUSS einen string zurück geben.