DotApp Router
Der Router ist eine zentrale Komponente des DotApp Frameworks und steuert das Routing von HTTP-Anfragen innerhalb der Anwendung. Er ermöglicht es Ihnen festzulegen, wie Anfragen (z. B. GET, POST) bestimmten Callback-Funktionen, Controllern oder Middleware zugeordnet werden. Der Router ist darauf ausgelegt, sowohl statische als auch dynamische Routen zu verarbeiten, Hooks (before und after) zu unterstützen und Flexibilität beim Aufbau von Webanwendungen zu bieten.
1.1 Was ist der Router?
Der Router im DotApp Framework ist die Klasse Dotsystems\App\Parts\Router, die eingehende HTTP-Anfragen verarbeitet und an die zuständigen Handler weiterleitet. Er arbeitet zusammen mit dem Request-Objekt, das Informationen zur Anfrage enthält (Pfad, Methode, Variablen). Seine Hauptaufgabe besteht darin, die Definition von Routen zu vereinfachen und sicherzustellen, dass für eine gegebene URL und HTTP-Methode der richtige Code ausgeführt wird.
Der Router ist direkt in den Kern des Frameworks integriert, sodass Sie ihn nicht separat installieren oder konfigurieren müssen – verwenden Sie einfach die Router-Fassade: Router::get(...).
1.2 Wichtige Funktionen
Der Router bietet eine Vielzahl von Funktionen, die die Anwendungsentwicklung erleichtern:
- Unterstützung von HTTP-Methoden: Definieren Sie Routen für
GET,POST,PUT,DELETE,PATCH,OPTIONS,HEAD,TRACEund die universelle MethodeANY. - Dynamische Routen: Verwenden Sie Variablen (z. B.
{id}) und reguläre Ausdrücke, um Teile der URL zu erfassen. - Middleware: Unterstützung für
before- undafter-Hooks, um Logik vor und nach dem Haupthandler auszuführen. - Request-Objekt: Übergibt Anfrageinformationen an Callbacks und Controller.
- Flexibilität: Möglichkeit, anonyme Funktionen, Controller oder Middleware über Zeichenketten zu nutzen (z. B.
"Module:Controller@method!"). - Verkettung: Methodenverkettung für übersichtlicheren Code.
1.3 Grundprinzipien des Routings
Der Router vergleicht die aktuelle URL (ermittelt über $request->getPath()) und die HTTP-Methode (über $request->getMethod()) mit den definierten Routen. Wird eine Übereinstimmung gefunden:
- Alle
before-Hooks werden ausgeführt. - Die Hauptlogik (Callback, Controller oder Middleware) wird ausgeführt.
- Alle
after-Hooks werden ausgeführt.
Routen können sein:
- Statisch: Exakte URL-Übereinstimmung (z. B.
/home). - Dynamisch: Enthalten Variablen oder Wildcards (z. B.
/user/{id}). - Erste Übereinstimmung gewinnt: Die erste passende Route wird verwendet; nachfolgende Treffer werden ignoriert.
Der Router löst Anfragen mit der Methode resolve() auf, die in der Regel automatisch im Lebenszyklus von DotApp aufgerufen wird.
Beispiel einer einfachen Route:
Registrieren Sie Routen in der Methode initialize($dotApp) eines Moduls in app/modules/{Module}/module.init.php. Controller liegen in app/modules/{Module}/Controllers/ und werden als 'Module:Controller@method!' aufgerufen.
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/home', function ($request) {
return "Welcome to the homepage!";
}, Router::STATIC_ROUTE);
Beim Aufruf der URL http://example.com/home wird der Text "Welcome to the homepage!" angezeigt.
2. Erste Schritte
Dieses Kapitel führt Sie durch die Grundlagen der Arbeit mit dem Router im DotApp Framework – von der Initialisierung bis zur Definition Ihrer ersten Route.
2.1 Initialisierung des Routers
Der Router ist ein Kernservice. Registrieren Sie Anwendungsrouten mit der Fassade Router:: aus der Methode initialize($dotApp) jedes Moduls. Controller liegen in app/modules/{Module}/Controllers/.
Technische Details:
- Der
Routerist eine Instanz der KlasseDotsystems\App\Parts\Router. - Beim Erzeugen erhält er
$dotAppObj(eine Instanz der HauptklasseDotApp) und damit Zugriff auf dasRequest-Objekt und andere Framework-Dienste.
2.2 Zugriff auf den Router in DotApp
Registrieren Sie Routen in module.init.php über die Router-Fassade. Das Objekt $request wird an Callbacks und Controller-Methoden übergeben und enthält den aktuellen Pfad, die Methode und die Variablen.
Beispiel für den Zugriff:
// Check the current path
echo $request->getPath(); // E.g., "/home"
// Check the HTTP method
echo $request->getMethod(); // E.g., "get"
2.3 Ihre erste Route definieren
Der einfachste Einstieg in den Router besteht darin, eine einfache Route mit einer der HTTP-Methoden (z. B. get()) zu definieren. Eine Route kann mit einer anonymen Funktion (Callback), einem Controller oder Middleware verknüpft werden.
Beispiel einer ersten Route mit einem Callback:
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/home', function ($request) {
return "Welcome to the homepage!";
}, Router::STATIC_ROUTE);
Erläuterung:
/home: Statischer URL-Pfad.function($request): Callback, der dasRequest-Objekt entgegennimmt und eine Antwort zurückgibt.- Beim Aufruf von
http://example.com/homewird der Text "Welcome to the homepage!" angezeigt.
Beispiel mit einem Controller:
Angenommen, Sie haben einen Controller Home in app/modules/HelloWorld/Controllers/Home.php mit einer Methode index:
// app/modules/HelloWorld/Controllers/Home.php
namespace Dotsystems\App\Modules\HelloWorld\Controllers;
class Home extends \Dotsystems\App\Parts\Controller {
public static function index($request) {
return "This is the homepage from the controller!";
}
}
// app/modules/HelloWorld/module.init.php
Router::get('/home', 'HelloWorld:Home@index!', Router::STATIC_ROUTE);
Erläuterung:
'HelloWorld:Home@index!': ModulHelloWorld, ControllerHome, statische Methodeindex. Das abschließende!deaktiviert DI für diese Methode.- Der
Routerlädt diese Methode automatisch und ruft sie mit demRequest-Objekt auf.
Routing ausführen:
Das Framework löst Routen auf, nachdem die Module geladen wurden. Anwendungsrouten werden in module.init.php deklariert.
// app/modules/HelloWorld/module.init.php
public function initialize($dotApp) {
Router::get('/home', function ($request) {
return "Welcome!";
}, Router::STATIC_ROUTE);
}
3. Routen definieren
Dieses Kapitel erklärt, wie Sie Routen im Router des DotApp Frameworks definieren. Der Router unterstützt verschiedene Definitionsarten – von grundlegenden HTTP-Methoden über dynamische Routen mit Variablen bis zur Arbeit mit Controllern.
3.1 Grundlegende HTTP-Methoden (GET, POST usw.)
Der Router stellt Methoden für alle gängigen HTTP-Methoden bereit: get(), post(), put(), delete(), patch(), options(), head() und trace(). Jede Methode definiert eine Route für eine bestimmte HTTP-Anfrage.
Beispiel einer GET-Route:
Router::get('/about', function ($request) {
return "This is the About Us page!";
});
Beispiel einer POST-Route:
Router::post('/submit', function ($request) {
return "The form has been submitted!";
});
Hinweis: Jede Methode nimmt als ersten Parameter den URL-Pfad und als zweiten Parameter einen Callback (oder eine Controller-Referenz) entgegen. Der Callback erhält stets das Objekt $request.
3.2 Die Methode match() für mehrere Methoden und URLs
Die Methode match() ermöglicht es, eine Route für mehrere HTTP-Methoden gleichzeitig oder für ein Array von URLs zu definieren. Das ist ein flexiblerer Ansatz als eigenständige Methoden wie get() oder post().
Beispiel mit mehreren Methoden:
Router::match(['get', 'post'], '/contact', function ($request) {
return "This is the contact page!";
});
Diese Route gilt sowohl für GET- als auch für POST-Anfragen an /contact.
Beispiel mit mehreren URLs:
Router::match(['get'], ['/home', '/index'], function ($request) {
return "Welcome to the homepage!";
});
Die Route erfasst Anfragen sowohl an /home als auch an /index.
3.3 Statische vs. dynamische Routen
Der Router unterscheidet zwischen statischen und dynamischen Routen:
- Statische Routen: Exakte URL-Übereinstimmung (z. B.
/home). - Dynamische Routen: Enthalten Variablen oder Wildcards (z. B.
/user/{id}).
Beispiel einer statischen Route:
Router::get('/profile', function ($request) {
return "This is a static profile!";
});
Beispiel einer dynamischen Route:
Router::get('/user/{id}', function ($request) {
return "User profile with ID: " . $request->matchData()['id'];
});
Für die URL /user/123 wird "User profile with ID: 123" angezeigt.
3.4 Variablen in Routen verwenden
Dynamische Routen können Variablen enthalten, die mit geschweiften Klammern gekennzeichnet sind (z. B. {id}). Diese Variablen werden automatisch extrahiert und stehen über $request->matchData() zur Verfügung.
Grundlegende Verwendung:
Router::get('/article/{slug}', function ($request) {
return "Article: " . $request->matchData()['slug'];
});
Für /article/how-to-cook wird "Article: how-to-cook" angezeigt.
Typisierte Variablen:
Der Router unterstützt außerdem eine Typisierung von Variablen:
{param:s}: Zeichenkette (ohne Schrägstriche).{param:i}: Ganzzahl.{param:l}: Nur Buchstaben.{param:s?}: Optionaler typisierter Parameter (das?gehört in die Klammern).{param*}: Wildcard (erfasst alles).
Router::get('/user/{id:i}', function ($request) {
return "User ID: " . $request->matchData()['id'];
});
Funktioniert nur für Zahlen, z. B. /user/123, aber nicht /user/abc.
3.5 Arbeiten mit Controllern und Middleware
Neben anonymen Funktionen können Sie Routen Controllern oder Middleware zuordnen, indem Sie eine Zeichenkette im Format "Module:Controller@method!" oder "#Module:Middleware@method!" verwenden.
Beispiel mit einem Controller:
// app/modules/HelloWorld/Controllers/User.php
namespace Dotsystems\App\Modules\HelloWorld\Controllers;
class User extends \Dotsystems\App\Parts\Controller {
public static function show($request) {
return "Displaying the user!";
}
}
// Route definition
Router::get('/user', 'HelloWorld:User@show!');
Beispiel mit Middleware:
// app/modules/HelloWorld/Middleware/AuthMiddleware.php
namespace Dotsystems\App\Modules\HelloWorld\Middleware;
class AuthMiddleware extends \Dotsystems\App\Parts\ModuleMiddleware {
public static function check($request) {
// Return a Response to short-circuit. Returning nothing continues the route.
if (!\Dotsystems\App\Parts\Auth::isLogged()) {
return new \Dotsystems\App\Parts\Response(403, 'Forbidden');
}
}
}
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/secure', 'HelloWorld:User@show!')
->before('#HelloWorld:AuthMiddleware@check!');
Hinweis: Funktionen müssen als public static definiert sein und $request als Parameter entgegennehmen. Middleware-Klassen werden fast immer mit ->before('#Module:Class@method!') angehängt und nicht als Haupthandler der Route verwendet.
4. Arbeiten mit dem Request-Objekt
Das Request-Objekt ist ein integraler Bestandteil des Router im DotApp Framework. Es trägt Informationen zur aktuellen HTTP-Anfrage und wird automatisch an Callbacks, Controller und Middleware übergeben. Dieses Kapitel erklärt, wie es funktioniert und wie Sie es effektiv nutzen.
4.1 Was ist Request?
Request ist eine Instanz der Klasse Dotsystems\App\Parts\Request und dient als Schnittstelle für die Arbeit mit Anfragedaten. Es enthält Informationen zum Pfad, zur HTTP-Methode, zu Variablen aus dynamischen Routen und zu weiteren Anfrageattributen. Es wird automatisch bei der Initialisierung des Router erzeugt und ist über $request zugänglich.
Wichtige Funktionen:
- Abrufen des aktuellen URL-Pfads und der Methode.
- Zugriff auf Variablen aus dynamischen Routen über
matchData(). - Übergabe von Daten an Callbacks und Hooks.
4.2 Daten aus Request abrufen
Das Request-Objekt stellt Methoden bereit, um grundlegende Anfrageinformationen abzurufen:
getPath(): Gibt den aktuellen URL-Pfad zurück (z. B./home).getMethod(): Gibt die HTTP-Methode zurück (z. B.get,post).matchData(): Gibt ein Array der aus einer dynamischen Route extrahierten Variablen zurück.hookData(): Gibt Daten zurück, die Hooks zugeordnet sind (verwendet mit eigenständigembefore/after).
Beispiel für den Zugriff:
Router::get('/user/{id}', function ($request) {
$path = $request->getPath(); // "/user/123"
$method = $request->getMethod(); // "get"
$id = $request->matchData()['id']; // "123"
return "Path: $path, Method: $method, ID: $id";
});
Für eine Anfrage an /user/123 wird angezeigt: "Path: /user/123, Method: get, ID: 123".
4.3 Request in Callbacks verwenden
Das Request-Objekt wird automatisch als Parameter an alle Callbacks, Controller und Middleware übergeben, die in Routen definiert sind. So können Sie direkt in der Routenlogik mit den Anfragedaten arbeiten.
Beispiel mit einer anonymen Funktion:
Router::get('/profile/{name}', function ($request) {
$name = $request->matchData()['name'];
return "Hello, $name!";
});
Für /profile/Jano wird angezeigt: "Hello, Jano!".
Beispiel mit einem Controller:
// app/modules/HelloWorld/Controllers/Profile.php
namespace Dotsystems\App\Modules\HelloWorld\Controllers;
class Profile extends \Dotsystems\App\Parts\Controller {
public static function show($request) {
$name = $request->matchData()['name'] ?? '';
return "Profile for: $name";
}
}
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/profile/{name}', 'HelloWorld:Profile@show!');
Das Ergebnis ist dasselbe wie bei der anonymen Funktion.
Beispiel mit Middleware:
// app/modules/HelloWorld/Middleware/CheckMiddleware.php
namespace Dotsystems\App\Modules\HelloWorld\Middleware;
class CheckMiddleware extends \Dotsystems\App\Parts\ModuleMiddleware {
public static function verify($request) {
// Optional logging. Do not return a Response unless you want to stop the request.
$path = $request->getPath();
}
}
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/check', 'HelloWorld:User@show!')
->before('#HelloWorld:CheckMiddleware@verify!');
Für /check wird zuerst die Middleware ausgeführt. Gibt sie kein Response zurück, wird der Handler User@show! ausgeführt.
Hinweis: matchData() gibt ein leeres Array zurück, wenn die Route keine dynamischen Variablen enthält. Prüfen Sie vor der Verwendung, ob ein Schlüssel existiert, z. B. isset($request->matchData()['id']), um Fehler zu vermeiden.
5. Middleware (Before- und After-Hooks)
Middleware im Router des DotApp Frameworks ermöglicht es Ihnen, zusätzliche Logik vor oder nach dem Haupthandler einer Route auszuführen. Diese „Hooks“ werden mit den Methoden before() und after() definiert und eignen sich ideal für Aufgaben wie Authentifizierung, Protokollierung oder die Anpassung der Antwort.
5.1 Was sind Hooks?
Hooks sind Funktionen, die automatisch in bestimmten Phasen der Routenverarbeitung ausgeführt werden:
before: Wird vor der Hauptlogik der Route ausgeführt (z. B. Callback oder Controller).after: Wird nach der Hauptlogik ausgeführt und hat Zugriff auf das Ergebnis der Route.
Hooks nehmen das Objekt $request als Parameter entgegen und können global, für eine bestimmte Route oder für eine Methode mit einer Route definiert werden.
5.2 before() definieren
Die Methode before() dient dazu, Logik hinzuzufügen, die vor dem Haupthandler ausgeführt wird. Sie lässt sich auf drei Arten anwenden:
- Global: Für alle Routen.
- Für eine bestimmte Route: Nur für einen gegebenen Pfad.
- Für eine Methode und Route: Speziell für eine HTTP-Methode und einen Pfad.
Globales Before:
Router::before(function ($request) {
return "Before every route!";
});
Router::get('/test', function ($request) {
return "Test page";
});
Der Hook wird für alle Routen ausgeführt, z. B. für /test wird zuerst "Before every route!" ausgeführt.
Before für eine bestimmte Route:
Router::get('/secure', function ($request) {
return "Secure page";
})->before(function ($request) {
return "Verifying access...";
});
Der Hook wird nur für /secure ausgeführt.
Before mit Methode und Route:
Router::before('get', '/login', function ($request) {
return "Checking login for GET";
});
Router::get('/login', function ($request) {
return "Login page";
});
5.3 after() definieren
Die Methode after() wird nach dem Haupthandler ausgeführt und hat dieselben Definitionsmöglichkeiten wie before(). Sie ist nützlich, um Ergebnisse anzupassen oder zu protokollieren.
Globales After:
Router::after(function ($request) {
return "After every route!";
});
Router::get('/test', function ($request) {
return "Test page";
});
Der Hook wird nach jeder Route ausgeführt, z. B. für /test wird zuerst "Test page" ausgeführt, gefolgt von "After every route!".
After für eine bestimmte Route:
Router::get('/profile', function ($request) {
return "Profile page";
})->after(function ($request) {
return "Profile has been displayed";
});
After mit Methode und Route:
Router::after('post', '/submit', function ($request) {
return "Form has been processed";
});
Router::post('/submit', function ($request) {
return "Submission successful";
});
5.4 Verwendung mit mehreren Routen
Sie können Hooks mehreren Routen gleichzeitig zuweisen, indem Sie ein Array von Pfaden mit der Methode match() verwenden oder before()/after() separat aufrufen.
Beispiel mit Match:
Router::match(['get'], ['/home', '/index'], function ($request) {
return "Homepage";
})->before(function ($request) {
return "Before the homepage";
})->after(function ($request) {
return "After the homepage";
});
Die Hooks gelten für beide Pfade: /home und /index.
Beispiel mit einem Array von Pfaden:
Router::before('get', ['/page1', '/page2'], function ($request) {
return "Before the pages";
});
Router::get('/page1', function ($request) {
return "Page 1";
});
Router::get('/page2', function ($request) {
return "Page 2";
});
Hinweis: Die Ausgabe der Hooks wird an die Antwort der Route angehängt. Um die Antwort zu ändern, arbeiten Sie im Hook direkt mit $request->response->body (mehr dazu in den erweiterten Funktionen).
6. Fehler- und Ausnahmebehandlung
Der Router im DotApp Framework ermöglicht es Entwicklern, Fehler und Ausnahmen zu steuern, die bei der Verarbeitung von Anfragen auftreten. Dieses Kapitel erklärt, wie Sie Standardfehler wie 404 behandeln und eigene Fehlerbehandlungslogik mit Callbacks und Hooks umsetzen.
6.1 Behandlung von 404-Fehlern
Findet der Router keine Übereinstimmung, löst er dotapp.router.resolve.404 aus. Behandelt kein Listener das Ereignis, kann Router::errorHandle(404, $view) die Ansicht error_{$view} rendern. Andernfalls sendet das Framework einen leeren 404-Status und beendet die Verarbeitung.
Empfohlen: Event-Listener in module.listeners.php
use Dotsystems\App\Parts\Events;
use Dotsystems\App\Parts\Response;
Events::on('dotapp.router.resolve.404', function () {
return new Response(404, 'Page not found');
});
Alternative: benannte Fehleransicht
Router::errorHandle(404, 'notfound');
Dabei wird nach einer Ansicht namens error_notfound gesucht. Registrieren Sie den Listener oder die Fehleransicht aus einem Modul.
6.2 Benutzerdefinierte Fehlerbehandlung
Entwickler können eigene Fehlerbehandlungslogik direkt in Callbacks oder Middleware mit Bedingungen und HTTP-Codes umsetzen.
Beispiel mit einer Bedingung in einem Callback:
Router::get('/user/{id:i}', function ($request) {
$id = $request->matchData()['id'];
if ($id > 100) {
http_response_code(403);
return "Access forbidden for IDs greater than 100!";
}
return "User profile: $id";
});
Für /user/150 wird "Access forbidden for IDs greater than 100!" mit dem Code 403 angezeigt.
Beispiel mit Middleware:
Router::get('/user/{id:i}', function ($request) {
$id = $request->matchData()['id'];
return "User profile: $id";
})->before(function ($request) {
$id = $request->matchData()['id'];
if (!isset($id)) {
http_response_code(400);
return "ID is missing!";
}
});
Für /user/ wird "ID is missing!" mit dem Code 400 angezeigt.
Hinweis: Mit http_response_code() in Callbacks oder Hooks können Sie eigene Fehlerzustände setzen. Es liegt beim Entwickler, ob das Skript mit exit beendet oder eine Fehlermeldung zurückgegeben wird.
7. Erweiterte Funktionen
Der Router im DotApp Framework bietet erweiterte Funktionen, die seine Möglichkeiten ausbauen. Dieses Kapitel behandelt Methodenverkettung, dynamischen URL-Abgleich, eine ausführliche Erklärung zum Erstellen dynamischer Adressen sowie die Definition von API-Endpunkten.
7.1 Methodenverkettung
Der Router unterstützt Methodenverkettung, sodass Sie Routen, Hooks und weitere Einstellungen in einem einzigen Aufruf definieren können. Das verbessert Lesbarkeit und Struktur des Codes.
Beispiel für Verkettung:
Router::get('/profile/{id}', function ($request) {
$id = $request->matchData()['id'];
return "Profile ID: $id";
})->before(function ($request) {
return "Checking before displaying the profile";
})->after(function ($request) {
return "Profile displayed";
});
Für /profile/123 werden nacheinander before, die Hauptlogik und after ausgeführt.
7.2 Dynamischer Routenabgleich (matchUrl())
Die Methode matchUrl() dient dem manuellen Abgleich einer URL mit einem Routing-Muster. Sie gibt ein Array der extrahierten Variablen zurück, wenn das Muster passt, andernfalls false. Das ist nützlich für eigene Validierungen oder zum Testen von Routen.
Beispiel zur Verwendung:
Router::get('/test', function ($request) {
$pattern = '/user/{id:i}';
$url = '/user/123';
$match = Router::matchUrl($pattern, $url);
if ($match !== false) {
return "Match! ID: " . $match['id'];
}
return "No match";
});
Für /test wird "Match! ID: 123" angezeigt.
7.3 Dynamische Adressen und Muster
Dynamische Adressen im Router ermöglichen es, Routen mit Variablen und optionalen Teilen über eine spezielle Syntax zu definieren. Diese Muster werden in allen Methoden einheitlich erkannt (z. B. get(), post(), match()), und die Variablen stehen über $request->matchData() zur Verfügung. Nachfolgend eine ausführliche Erklärung mit einem Beispiel und einer Liste der häufigsten Muster.
Beispiel einer dynamischen Adresse:
Router::get('/documentation/intro(?:/{language})?', function ($request) {
$language = $request->matchData()['language'] ?? 'default';
return "Introductory documentation, language: $language";
});
Erläuterung:
/documentation/intro(?:/{language})?: Definiert eine Route, in der{language}ein optionaler Teil ist (gekennzeichnet durch?:und?)./documentation/intro: Gültig (Sprache ist "default")./documentation/intro/eng: Gültig (Sprache ist "eng")./documentation/intro/: Ungültig (derRoutererwartet nach dem Schrägstrich einen Wert, sofern dieser vorhanden ist).
Die Variable language wird in $request->matchData() extrahiert, wenn sie angegeben ist, andernfalls ist sie null.
Die am häufigsten verwendeten Muster:
Hier eine Liste von 10 gängigen Mustern dynamischer Adressen in Webanwendungen, mit Beispielen und Erläuterungen:
/{resource}/{id:i}- Einfache CRUD-RouteRouter::get('/users/{id:i}', function ($request) { return "User ID: " . $request->matchData()['id']; });Gültig:
/users/123, Ungültig:/users/abc/{category}/{slug:s}- Kategorie und Artikel-SlugRouter::get('/blog/{category}/{slug:s}', function ($request) { return "Category: " . $request->matchData()['category'] . ", Slug: " . $request->matchData()['slug']; });Gültig:
/blog/tech/how-to-code/api/v{version}/{endpoint}- Versionierte APIRouter::get('/api/v{version}/{endpoint}', function ($request) { return "API v" . $request->matchData()['version'] . ": " . $request->matchData()['endpoint']; });Gültig:
/api/v1/users/{page}(?:/{subpage})?- Optionale UnterseiteRouter::get('/docs/{page}(?:/{subpage})?', function ($request) { $subpage = $request->matchData()['subpage'] ?? 'main'; return "Page: " . $request->matchData()['page'] . ", Subpage: $subpage"; });Gültig:
/docs/intro,/docs/intro/setup/{type}/{id:i}/{action}- Aktion auf einer RessourceRouter::get('/posts/{id:i}/{action}', function ($request) { return "ID: " . $request->matchData()['id'] . ", Action: " . $request->matchData()['action']; });Gültig:
/posts/5/edit/{resource}/{filter:s?}- Optionaler FilterRouter::get('/products/{filter:s?}', function ($request) { $filter = $request->matchData()['filter'] ?? 'all'; return "Products, filter: $filter"; });Gültig:
/products,/products/new/{path*}- Wildcard für den gesamten PfadRouter::get('/files/{path*}', function ($request) { return "File path: " . $request->matchData()['path']; });Gültig:
/files/images/photo.jpg/{lang:l}/{section}- Sprache und AbschnittRouter::get('/{lang:l}/{section}', function ($request) { return "Language: " . $request->matchData()['lang'] . ", Section: " . $request->matchData()['section']; });Gültig:
/en/news, Ungültig:/123/news/search(?:/{query})?- Optionale SuchanfrageRouter::get('/search(?:/{query})?', function ($request) { $query = $request->matchData()['query'] ?? 'empty'; return "Search: $query"; });Gültig:
/search,/search/php/{resource}/{id:i}(?:/{extra})?- Ressource mit optionalem ParameterRouter::get('/users/{id:i}(?:/{extra})?', function ($request) { $extra = $request->matchData()['extra'] ?? 'none'; return "ID: " . $request->matchData()['id'] . ", Extra: $extra"; });Gültig:
/users/10,/users/10/details
Hinweis: Diese Muster sind flexibel und kombinierbar. Verwenden Sie {?:} für optionale Teile und Typen (:i, :s, :l) für präzise Einschränkungen.
7.4 API-Endpunkte mit apiPoint definieren
Die Methode apiPoint im Router bietet eine praktische Möglichkeit, API-Endpunkte mit Unterstützung für Versionierung, Module und dynamische Parameter zu definieren. Sie ermöglicht flexible eigene Pfade und Methoden. Zusammen mit der integrierten abstrakten Klasse Controller und ihren Methoden apiDispatch (Hauptlogik) und api (kürzerer Alias) können Anfragen automatisch an bestimmte Controller-Methoden mit Unterstützung für Dependency Injection (DI) weitergeleitet werden.
Definition
Router::apiPoint($version, $module, $controller, $custom = null);
Parameter:
$version: API-Version (z. B."1"für v1).$module: Modulname (z. B."shop").$controller: Callback oder Zeichenkette im Format"Module:Controller@method!"(z. B."HelloWorld:Posts@apiDispatch!","HelloWorld:Posts@api!"oder eine eigene Methode).$custom(optional): Bestimmter Pfad (Zeichenkette) oder Array von Pfaden. Unterstützt reguläre Ausdrücke (z. B.(?:/{id})?).
Wird $custom nicht angegeben, gilt der Standardpfad /api/v{version}/{module}/{resource}(?:/{id})?. Ist er angegeben, werden nur die Pfade aus $custom verwendet. Die erste Route gewinnt! Statische Pfade müssen vor dynamischen stehen, damit sie nicht von dynamischer Logik überschrieben werden.
Integrierter Controller und die Methoden apiDispatch/api:
Das Framework stellt die abstrakte Klasse Dotsystems\App\Parts\Controller mit der Methode apiDispatch bereit, die Anfragen automatisch an bestimmte Methoden im Format weiterleitet (z. B. postUsers, getPosts), basierend auf der HTTP-Methode und dem Wert des dynamischen Parameters resource. Verweisen Sie apiPoint auf Controller@apiDispatch (oder auf Controller@api als kürzeren Alias). Die automatische Weiterleitung funktioniert, wenn der Pfad {resource} enthält. Sie registrieren keine eigene Route für jede Ressourcenmethode.
apiDispatch ordnet HTTP-Methode plus Ressource einer Controller-Methode namens {method}{Resource} zu, zum Beispiel GET …/posts → getPosts($request). Implementieren Sie diese Methoden als public static. Passt keine Methode, wird error404($request) ausgeführt, sofern vorhanden; andernfalls gibt das Framework HTTP 404 zurück. Registrieren Sie den Dispatcher mit einem abschließenden !:
Router::apiPoint("1", "shop", "HelloWorld:Posts@apiDispatch!");
Fehler anpassen:
Existiert die Zielmethode (z. B. postUsers) nicht, prüft apiDispatch zuerst, ob der Controller eine Methode error404 definiert. Ist das der Fall, wird sie aufgerufen, sodass Sie eigene Logik für 404-Fehler festlegen können (z. B. JSON-Antwort, Protokollierung). Fehlt error404, wird eine Standardfehlermeldung mit HTTP-Code 404 zurückgegeben.
Verwendung mit automatischem Dispatching:
Das automatische Dispatching über apiDispatch (oder api) funktioniert nur, wenn der Pfad den dynamischen Parameter {resource} an der richtigen Stelle enthält (z. B. /api/v1/shop/{resource}). Hält $custom dieses Format nicht ein, greift die Automatik nicht, und es muss eine eigene Methode verwendet werden.
Beispiel ohne $custom (automatisches Dispatching):
Router::apiPoint("1", "shop", "HelloWorld:Posts@apiDispatch!");
Resultierende Pfade:
POST /api/v1/shop/users- RuftpostUsersauf, sofern vorhanden.GET /api/v1/shop/posts- RuftgetPostsauf.GET /api/v1/shop/status- Rufterror404auf, sofern vorhanden, andernfalls 404 mit einer Standardmeldung./api/v1/shop/posts/- Wird nicht erfasst.
Beispiel mit $custom und automatischem Dispatching:
Router::apiPoint("1", "shop", "HelloWorld:Posts@apiDispatch!", ["{resource}(?:/{id})?/details"]);
Resultierende Pfade:
POST /api/v1/shop/users/details- RuftpostUsersauf.GET /api/v1/shop/posts/details- RuftgetPostsauf.GET /api/v1/shop/posts/abc123/details- RuftgetPostsauf.PUT /api/v1/shop/status/details- Rufterror404auf, sofern vorhanden, andernfalls 404 mit einer Standardmeldung./api/v1/shop/users/- Wird nicht erfasst.
Beispiel mit benutzerdefinierten Routen und einer eigenen Methode:
Router::apiPoint("1", "shop", "HelloWorld:Posts@customMethod!", ["users/details", "posts/summary"]);
Resultierende Pfade: Automatisches Dispatching greift hier nicht, weil {resource} fehlt. Die Logik hängt von der Implementierung von customMethod ab.
GET /api/v1/shop/users/details- RuftcustomMethodauf.POST /api/v1/shop/posts/summary- RuftcustomMethodauf.
Beispiel-Controller mit DI und benutzerdefiniertem Fehler:
namespace Dotsystems\App\Modules\Dotcmsfe\Controllers;
class Posts extends \Dotsystems\App\Parts\Controller {
public static function postUsers($request, \SomeService $service) {
return "Creating users: " . $service->process($request->getPath());
}
public static function getPosts($request) {
$id = $request->matchData()['id'] ?? null;
return "List of posts" . ($id ? " with ID: $id" : "");
}
public static function error404($request) {
http_response_code(404);
return json_encode([
'error' => 'Not Found',
'message' => "Resource '{$request->matchData()['resource']}' not found or method '{$request->getMethod()}' not supported",
'path' => $request->getPath()
]);
}
public static function customMethod($request) {
return "Custom method for path: " . $request->getPath();
}
}
Hinweis: Der integrierte Controller vereinfacht die API-Verarbeitung mit apiDispatch (oder api), wenn der Pfad {resource} enthält. Für eigene Routen ohne {resource} können Sie eigene Methoden verwenden, das automatische Dispatching greift dann jedoch nicht. Die Reihenfolge der Pfade in $custom ist entscheidend – statische Pfade müssen vor dynamischen stehen.
8. Praktische Beispiele
Dieses Kapitel zeigt praktische Beispiele für die Verwendung des Router im DotApp Framework. Es verdeutlicht, wie Sie grundlegende und erweiterte Funktionen kombinieren, um typische Szenarien in Webanwendungen umzusetzen.
8.1 Einfache GET-Route
Das grundlegendste Beispiel: Definition einer statischen Route mit einer einfachen Antwort.
Beispiel:
Router::get('/welcome', function ($request) {
return "Welcome to the application!";
});
Für eine Anfrage an /welcome wird angezeigt: "Welcome to the application!".
Anwendungsfall: Ideal für statische Seiten wie Startseiten oder „Über uns“.
8.2 Dynamische Route mit Variablen
Ein Beispiel für eine dynamische Route mit Variablenextraktion zur Anzeige von Benutzerdaten.
Beispiel:
Router::get('/user/{id:i}/{name}', function ($request) {
$id = $request->matchData()['id'];
$name = $request->matchData()['name'];
return "User ID: $id, Name: $name";
});
Für /user/123/Jano wird angezeigt: "User ID: 123, Name: Jano".
Anwendungsfall: Geeignet für Profile, Produktdetails oder andere Ressourcen mit Kennungen.
8.3 Middleware verwenden
Ein Beispiel, das eine Route mit before- und after-Hooks für Prüfung und Protokollierung kombiniert.
Beispiel:
Router::get('/dashboard', function ($request) {
return "Welcome to the dashboard!";
})->before(function ($request) {
$user = "guest"; // Simulated verification
if ($user === "guest") {
http_response_code(403);
return "Access denied!";
}
})->after(function ($request) {
return "Dashboard displayed at " . date('H:i:s');
});
Für /dashboard wird "Access denied!" mit dem Code 403 angezeigt (da die simulierte Prüfung fehlschlägt). Wäre die Prüfung erfolgreich, würde "Welcome to the dashboard!" gefolgt von der Anzeigezeit erscheinen.
Anwendungsfall: Authentifizierung, Zugriffsprotokollierung oder Anpassung der Antwort.
8.4 Kombination mit Controllern
Ein Beispiel für die Anbindung einer Route an einen Controller, um Logik vom Routing zu trennen.
Beispiel:
// app/modules/HelloWorld/Controllers/Article.php
namespace Dotsystems\App\Modules\HelloWorld\Controllers;
class Article extends \Dotsystems\App\Parts\Controller {
public static function detail($request) {
$slug = $request->matchData()['slug'];
return "Article detail: $slug";
}
}
// Route definition
Router::get('/article/{slug:s}', 'HelloWorld:Article@detail!');
Für /article/how-to-code wird angezeigt: "Article detail: how-to-code".
Anwendungsfall: Größere Anwendungen, in denen die Organisation des Codes in Controllern erforderlich ist.
9. Tipps und Tricks
Dieses Kapitel bietet praktische Tipps und Tricks für den effektiven Einsatz des Router im DotApp Framework. Sie helfen Ihnen, Code zu optimieren, Probleme zu debuggen und bewährte Vorgehensweisen einzuhalten.
9.1 Routing optimieren
Der Router im DotApp Framework arbeitet nach dem Prinzip „erste Übereinstimmung gewinnt“ – die erste passende Route in der Reihenfolge der Definition wird verwendet, alle weiteren werden ignoriert, unabhängig davon, ob sie statisch oder dynamisch sind. Die Definitionsreihenfolge ist daher entscheidend für die Optimierung.
- Definieren Sie die wichtigsten Routen zuerst: Da die erste Übereinstimmung gewinnt, platzieren Sie kritische oder häufig genutzte Routen oben.
- Verwenden Sie spezifische Muster: Z. B.
{id:i}statt{id}, um unbeabsichtigte Treffer auf falschen Routen zu vermeiden. - Gruppieren Sie ähnliche Routen: Verwenden Sie
match()mit einem Array von Pfaden, um Duplikate zu reduzieren, achten Sie aber auf die Reihenfolge.
Beispiel für die Optimierung:
Router::get('/user/{id:i}', function ($request) { // First dynamic route
return "Dynamic user ID: " . $request->matchData()['id'];
});
Router::get('/user/123', function ($request) { // Second static route
return "Static user 123";
});
Für /user/123 gewinnt immer die erste Route ("Dynamic user ID: 123"), weil sie zuerst definiert wurde, obwohl die zweite statisch und genauer ist. Um der statischen Route Vorrang zu geben, definieren Sie sie früher.
Beispiel mit geänderter Priorität:
Router::get('/user/123', function ($request) { // First static route
return "Static user 123";
});
Router::get('/user/{id:i}', function ($request) { // Second dynamic route
return "Dynamic user ID: " . $request->matchData()['id'];
});
Nun wird für /user/123 "Static user 123" angezeigt, weil diese Route zuerst definiert ist.
9.2 Routen debuggen
Bei Problemen mit dem Routing nutzen Sie die im Router und in PHP verfügbaren Werkzeuge, um festzustellen, welche Route tatsächlich ausgelöst wird – insbesondere wegen der Regel „erste Übereinstimmung“.
- Pfad prüfen: Verwenden Sie
$request->getPath(), um die URL zu überprüfen, die derRouterverarbeitet. - Variablen ausgeben: Geben Sie
$request->matchData()aus, um zu sehen, welche Werte extrahiert wurden. - Reihenfolge testen: Fügen Sie temporäre Ausgaben (z. B.
echo) in Callbacks ein, um festzustellen, welche Route ausgeführt wurde.
Beispiel zum Debuggen:
Router::get('/page/{id}', function ($request) {
echo "Dynamic route triggered for ID: " . $request->matchData()['id'];
return "Dynamic page " . $request->matchData()['id'];
});
Router::get('/page/1', function ($request) {
echo "Static route triggered for /page/1";
return "Static page 1";
});
Für /page/1 wird "Dynamic route triggered for ID: 1" und "Dynamic page 1" angezeigt, weil die dynamische Route zuerst definiert ist. Eine Änderung der Reihenfolge würde der statischen Route Vorrang geben.
9.3 Best Practices für die Routenstruktur
Bewährte Vorgehensweisen helfen, Klarheit und Vorhersagbarkeit im Routing zu bewahren.
- Logische Reihenfolge: Definieren Sie Routen von der spezifischsten zur allgemeinsten, um die Regel „erste Übereinstimmung gewinnt“ zu nutzen.
- Kommentare: Fügen Sie Kommentare über Routen hinzu, um zu erläutern, warum sie in einer bestimmten Reihenfolge stehen.
- Logik trennen: Verwenden Sie Controller für komplexe Routen statt Inline-Callbacks.
Beispiel für Best Practices:
// Most specific static route
Router::get('/api/users/guest', function ($request) {
return "Guest user";
});
// Specific dynamic route
Router::get('/api/users/{id:i}', function ($request) {
return "User ID: " . $request->matchData()['id'];
});
// General route last
Router::get('/api/{resource}', function ($request) {
return "Resource: " . $request->matchData()['resource'];
});
Für /api/users/guest greift die erste Route; für /api/users/5 die zweite; und für /api/products die dritte – dank der logischen Reihenfolge.
10. Fazit
Dieses Kapitel schließt die Dokumentation zum Router im DotApp Framework ab. Es fasst die Vorteile zusammen und blickt auf die künftige Entwicklung und die Community.
10.1 Warum den Router in DotApp verwenden?
Der Router im DotApp Framework ist ein einfaches, aber leistungsfähiges Werkzeug zur Steuerung des Routings in Webanwendungen. Zu den wichtigsten Vorteilen zählen:
- Flexibilität: Unterstützung für statische und dynamische Routen mit Variablen und optionalen Teilen.
- Einfachheit: Intuitive Schnittstelle zur Definition von Routen über HTTP-Methoden wie
get()undpost(). - Middleware: Möglichkeit,
before- undafter-Hooks für erweiterte Logik hinzuzufügen. - Erste Übereinstimmung gewinnt: Vorhersehbares Verhalten anhand der Definitionsreihenfolge der Routen, das Entwicklern volle Kontrolle gibt.
- Integration: Nahtlose Zusammenarbeit mit Controllern und dem
Request-Objekt bei der Anfrageverarbeitung.
Ob Sie eine kleine Anwendung oder ein komplexes System aufbauen: Der Router stellt die Werkzeuge bereit, um Anfragen schnell und effizient der Logik zuzuordnen.