2021
PHP Vertiefung

PHP - Namespace, Autoloader, Message Handler und ORM

Datum: November 2021
Lesedauer: 3 Minuten


Mein letzter Blogpost handelte von den PHP-Grundlagen. Nun schreibe ich vertiefter über vier weitere Themen:

  • Namespace
  • Autoloader
  • Message Handler
  • Object Relational Mapper (ORM)

Namespace

Namensräume bieten zwei bedeutungsvolle Vorteile:

  1. Elemente (Klassen, Funktionen und Konstanten) können denselben Namen haben.
  2. Code kann in logische Gruppen unterteilt werden.

Dabei treten mithilfe der namespaces keine collisions auf.

Verwendung

Um von den Vorteilen der namespaces Gebrauch zu machen, muss man das namespace-Keyword verwenden.

Database.php
<?php
namespace Data;
 
class Database {
   public function __construct() {
      echo "Fully qualified class name: ".__CLASS__."\n";
   }
}
?>

Nachdem man die Klasse mit use eingebunden hat, kann man sie verwenden.

app.php
<?php
use Data\Database;
require "Database.php";
$db = new Database();
?>

Autoloader

Im obigen Beispiel kann man erkennen, dass die Klassen, welche man verwenden will, inkludiert werden müssen. Das manuelle Einbinden hat jedoch zwei
Probleme:

  1. Aufwand – Hat man viele Klassen, kostet das Einbinden viel Zeit.
  2. Fehleranfälligkeit – Verschiebt man eine Klasse, muss man den Pfad anpassen.

Ein autoloader löst diese Probleme, indem er die entsprechenden Dateien sucht und korrekt einbindet.

autoloader.php
function autoload($className) {
    $className = ltrim('classes/' . $className, '\\');
    $fileName = '';
    $namespace = '';
    if ($lastNsPos = strrpos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
 
    require $fileName;
}
 
spl_autoload_register('autoload');

Hier wird auf Zeile 2 definiert, dass sich die Klassen im Ordner classes befinden.

Message Handler

Bei meiner Webanwendung kann der User Daten bearbeiten. Beim Speichern soll er Feedback erhalten.
Mein initialer Ansatz hatte jedoch zwei Probleme:

  1. Es konnten keine Nachrichten gesammelt werden.
  2. Das Filtern nach Status oder farbliche Darstellung im Frontend war nicht möglich.

Deshalb habe ich einen message handler implementiert.

Message-Klasse

Die Message-Klasse speichert Status und Nachricht.

Message.php
<?php
namespace MessageHandler;
 
class Message {
    private string $message;
    private string $status;
    public const SUCCESS = "success";
    public const WARNING = "warning";
    public const ERROR = "error";
 
    public function __construct(string $message, string $status) {
        $this->message = $message;
        $this->status = $status;
    }
 
    public function getMessage(): string {
        return $this->message;
    }
 
    public function getStatus(): string {
        return $this->status;
    }
}
?>

MessageHandler-Klasse

Der MessageHandler verwaltet Nachrichten und speichert sie in einem Array.

MessageHandler.php
<?php
namespace MessageHandler;
 
class MessageHandler {
    private array $messages = [];
 
    public function getMessages(): array {
        return $this->messages;
    }
 
    public function getErrorMessages(): array {
        return array_filter($this->messages, fn($m) => $m->getStatus() == Message::ERROR);
    }
 
    public function getWarningMessages(): array {
        return array_filter($this->messages, fn($m) => $m->getStatus() == Message::WARNING);
    }
 
    public function getSuccessMessages(): array {
        return array_filter($this->messages, fn($m) => $m->getStatus() == Message::SUCCESS);
    }
 
    public function addMessage(string $message, string $status): void {
        $this->messages[] = new Message($message, $status);
    }
}
?>

Auf Anfrage können bestimmte Nachrichten abgerufen werden.

editProduct.php
<?php foreach ($messageHandler->getSuccessMessages() as $message): ?>
    <p style="font-size: 20px; color: mediumseagreen;"><?= $message->getMessage(); ?></p>
<?php endforeach; ?>

Object Relational Mapper (ORM)

ORM speichert Daten aus der Datenbank in Objekten (mapping). Danach kann man CRUD-Operationen über diese Objekte tätigen.

Beispiel

In meinem Projekt habe ich eine Database-Klasse erstellt.

Connect

Will man die MySQL Datenbank verwenden, kann die connect Methode aufrufen.

Connect
public function connect() {
    $this->mysqli = new mysqli("localhost", "root", "root", "apparel");
}

Disconnect

Auch um die Verbindung zu trennen, muss nur die disconnect Funktion aufgerufen werden.

public function disconnect() {
    $this->mysqli->close();
}

CRUD

Auch zur Bewältigung von CRUD Operationen kann man die ORM Klasse verwenden.
In diesem Beispiel bekommt man die gewünschten Künstler aus der Datenbank.

public function getArtists($ids = null): array {
    $artistList = [];
    $query = "SELECT * FROM artist" . ($ids ? " WHERE artist_id IN ($ids)" : "");
    $artistResponse = $this->mysqli->query($query);
 
    while ($row = $artistResponse->fetch_assoc()) {
        $artistList[] = new Artist($row["type"], $row["name"], $row["age"], $row["residence"], $row["nationality"], $row["artist_id"]);
    }
    return $artistList;
}

Hier wird eine Brand gelöscht.

public function deleteBrand(Brand $brand) {
    $id = $brand->getId();
    $stmt = $this->mysqli->prepare("DELETE FROM brand WHERE brand_id = ?");
    $stmt->bind_param("i", $id);
    $stmt->execute();
}

Auf der Zeile 4 wird mithilfe von bind_param die $id in die SQL-Syntax eingefügt, damit diese danach ausgeführt werden kann.