2021
PHP In-Depth

PHP - Namespace, Autoloader, Message Handler, and ORM

Translated from German using ChatGPT.

Date: November 2021
Reading time: 3 minutes


My last blog post was about PHP basics. Now I will write in more detail about four other topics:

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

Namespace

Namespaces offer two significant advantages:

  1. Elements (classes, functions and constants) can have the same name.
  2. Code can be divided into logical groups.

No collisions occur with the help of namespaces.

Usage

To make use of the advantages of namespaces, you must use the namespace keyword.

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

After you have integrated the class with use, you can use it.

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

Autoloader

In the example above, you can see that the classes you want to use must be included. However, manual inclusion has two
problems:

  1. Expense - if you have a lot of classes, including them takes a lot of time.
  2. Error-proneness - if you move a class, you have to adjust the path.

An autoloader solves these problems by searching for the corresponding files and integrating them correctly.

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');

Here, line 2 defines that the classes are located in the classes folder.

Message Handler

In my web application, the user can edit data. They should receive feedback when saving.
However, my initial approach had two problems:

  1. No messages could be collected.
  2. Filtering by status or color display in the frontend was not possible.

Therefore I implemented a message handler.

Message class

The Message class stores status and message.

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 class

The MessageHandler manages messages and stores them in an 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);
    }
}
?>

Certain messages can be retrieved on request.

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 stores data from the database in objects (mapping). CRUD operations can then be performed on these objects.

Example

In my project I have created a Database class.

Connect

If you want to use the MySQL database, you can call the connect method.

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

Disconnect

The disconnect function must also be called to disconnect the connection.

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

CRUD

The ORM class can also be used to handle CRUD operations.
In this example, you get the desired artists from the database.

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;
}

A fire is extinguished here.

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();
}

On line 4, bind_param is used to insert the $id into the SQL syntax so that it can then be executed.