Routing im CleverStyle Framework

  • Tutorial
Viele Aspekte des CleverStyle Frameworks haben eine alternative Implementierung der gleichen Dinge wie die meisten anderen Frameworks.

In diesem Artikel werden das Routing-Gerät, Anwendungsbeispiele sowie Beispiele für Eingriffe in diesen Mechanismus ausführlich beschrieben. Falls gewünscht, ersetzen Sie ihn vollständig durch Ihren eigenen.

Hauptunterschied


Der Hauptunterschied zwischen Routing und Implementierungen in gängigen Frameworks wie Symfony, Laravel oder Yii ist Deklarativität statt Imperativ.

Dies bedeutet, dass anstatt Routen in einem bestimmten Format anzugeben und einer Route eine bestimmte Klasse, Methode oder einen bestimmten Abschluss zuzuordnen, lediglich die Struktur der Routen beschrieben wird. Diese Struktur reicht aus, um zu verstehen, welcher Code abhängig von der Route ausgeführt wird.

Ein solcher Ansatz von Konventionen anstelle von Konfigurationen ist in dem Sinne zweckmäßig, dass beim Schreiben von Code weniger Aufwand erforderlich ist und keine Anzeige der Konfiguration erforderlich ist, um zu verstehen, welcher Code beim Öffnen einer bestimmten Seite aufgerufen wird, wie dies aus der im Framework angenommenen Vereinbarung hervorgeht.

Routing-Grundlagen


Jede URL in der Framework-Ansicht ist in mehrere Teile unterteilt. Zu Beginn werden vor jeder Verarbeitung die Anforderungsparameter ( ?und alles, was danach folgt ) aus dem Seitenpfad entfernt .

Als nächstes erhalten wir das allgemeine Format des Pfads der folgenden Form ( |verwendet, um die Auswahl mehrerer Optionen zu trennen, []optionale unabhängige Komponenten des Pfads werden gruppiert), das Beispiel wird der Einfachheit halber in mehrere Zeilen unterteilt, bevor der Pfad in Schrägstriche aufgeteilt und in ein Array von Teilen des ursprünglichen Pfads umgewandelt wird:

[language/]
[admin/|api/|cli/]
[Module_name
    [/path
        [/sub_path
            [/id1
                [/another_subpath
                    [/id2]
                ]
            ]
        ]
    ]
]

Anzahl der Ebenen Verschachtelung ist nicht begrenzt.

Der erste Schritt besteht darin, das Sprachpräfix zu überprüfen. Es nimmt nicht am Routing teil (und ist möglicherweise nicht vorhanden), hat jedoch Einfluss darauf, welche Sprache auf der Seite verwendet wird. Das Format hängt von den verwendeten Sprachen und deren Anzahl ab. Es kann einfach sein ( en, ru) oder die Region berücksichtigen ( en_gb, ru_ua).

Nach der Sprache befindet sich ein optionaler Teil, der den Seitentyp bestimmt. Dies kann eine Administrationsseite ( $Request->admin_path === true), eine Anforderung an die API ( $Request->api_path === true), eine Anforderung an die CLI-Schnittstelle ( $Request->cli_path === true) oder eine reguläre Benutzerseite sein, wenn sie nicht explizit angegeben wird.

Als nächstes wird das Modul bestimmt, das die Seite verarbeitet. Später ist dieses Modul als verfügbar $Request->current_module.

Es ist erwähnenswert, dass der Name des Moduls beispielsweise für das Modul lokalisiert werden kannMy_blogEs gibt ein paar in den Übersetzungen "My_blog" : "Мой блог", dann können Sie es als Name des Moduls verwenden Мой_блог, aber immer noch $Request->current_module === 'My_blog'.

Der Rest der Array-Elemente nach dem Modul fällt in $Request->route, die von den Modulen zum Beispiel für benutzerdefiniertes Routing verwendet werden können.

Bevor Sie mit den nächsten Schritten fortfahren, werden 2 weitere Arrays gefüllt.

$Request->route_idsenthält Elemente von $Request->route, die Ganzzahlen sind (es versteht sich, dass dies Bezeichner sind), $Request->route_pathenthält jedoch alle Elemente mit $Request->routeAusnahme von Ganzzahlen und wird als Route innerhalb des Moduls verwendet.

So kommen Sie frühzeitig zum Routing


Dem Entwickler stehen eine Reihe von Ereignissen zur Verfügung, die es ihm ermöglichen, sich bereits in diesen Phasen zu verkeilen und das Verhalten nach eigenem Ermessen zu ändern.

Das Ereignis wird System/Request/routing_replace/beforeunmittelbar vor dem Bestimmen der Sprache der Seite ausgelöst und ermöglicht es Ihnen, den ursprünglichen Pfad in Form einer Zeichenfolge zu ändern. Die Manipulationen der untersten Ebene können an dieser Stelle ausgeführt werden.

Das Ereignis wird System/Request/routing_replace/afternach der Formation ausgelöst $Request->route_idsund $Request->route_pathermöglicht es Ihnen, wichtige Parameter anzupassen, nachdem sie vom System bestimmt wurden.

Ein Beispiel für das Hinzufügen von UUID-Unterstützung als Alternative zu Standard-Integer-IDs:

Event::instance()->on(
    'System/Request/routing_replace/after',
    function ($data) {
        $route_path = [];
        $route_ids  = [];
        foreach ($data['route'] as $item) {
            if (preg_match('/([a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}?)/i', $item)) {
                $route_ids[] = $item;
            } else {
                $route_path[] = $item;
            }
        }
        if ($route_ids) {
            $data['route_path'] = $route_path;
            $data['route_ids']  = $route_ids;
        }
    }
);

Routenstruktur


Die Routenstruktur ist eine baumartige JSON-Struktur, bei der der Schlüssel jeder untergeordneten Ebene eine Erweiterung der übergeordneten Ebene ist. Einige Endknoten sind möglicherweise leer, wenn die benachbarten Knoten eine tiefere Struktur aufweisen.

Ein Beispiel für die aktuelle Systemmodul-API-Struktur:

{
    "admin"     : {
        "about_server" : [],
        "blocks"       : [],
        "databases"    : [],
        "groups"       : [
            "_",
            "permissions"
        ],
        "languages"    : [],
        "mail"         : [],
        "modules"      : [],
        "optimization" : [],
        "permissions"  : [
            "_",
            "for_item"
        ],
        "security"     : [],
        "site_info"    : [],
        "storages"     : [],
        "system"       : [],
        "themes"       : [],
        "upload"       : [],
        "users"        : [
            "_",
            "general",
            "groups",
            "permissions"
        ]
    },
    "blank"     : [],
    "languages" : [],
    "profile"   : [],
    "profiles"  : [],
    "timezones" : []
}

Beispiele für (echte) Abfragen, die dieser Struktur entsprechen:

GET            api/System/blank
GET            api/System/admin/about_server
SEARCH_OPTIONS api/System/admin/users
SEARCH         api/System/admin/users
PATCH          api/System/admin/users/42
GET            api/System/admin/users/42/groups
PUT            api/System/admin/users/42/permissions

Den endgültigen Weg finden


Das Abrufen der Route vom Seitenpfad ist nur der erste von zwei Schritten. Die zweite Stufe berücksichtigt die Konfiguration des aktuellen Moduls und passt die endgültige Route entsprechend an.

Wofür ist das? Angenommen, ein Benutzer öffnet eine Seite /Blogsund die Routenstruktur ist wie folgt konfiguriert ( modules/Blogs/index.json):

[
    "latest_posts",
    "section",
    "post",
    "tag",
    "new_post",
    "edit_post",
    "drafts",
    "atom.xml"
]

In diesem Fall $Request->route_path === []aber $App->controller_path === ['index', 'latest_posts'].

indexwird hier unabhängig von dem Modul und der Konfiguration sein, aber es latest_postshängt bereits von der Konfiguration ab. Wenn es sich bei der Seite nicht um eine API oder eine CLI-Anforderung handelt, wählt das Framework bei der Angabe einer unvollständigen Route den ersten Schlüssel aus der Konfiguration auf jeder Ebene aus, bis er das Ende tief in der Struktur erreicht. Das ist Blogsähnlich Blogs/latest_posts.

Für API- und CLI-Anforderungen in diesem Sinne gibt es einen Unterschied: Das Auslassen von Teilen der Route auf diese Weise ist verboten und nur zulässig, wenn es als erstes Element in der Struktur auf der entsprechenden Ebene verwendet wird _.

Beispielsweise können wir für eine API die folgende Struktur ( modules/Module_name/api/index.json) haben:

{
    "_"        : []
    "comments" : []
}

In diesem Fall das api/Module_namegleiche api/Module_name/_. Auf diese Weise können Sie APIs mit schönen Methoden erstellen (denken Sie daran, dass wir Bezeichner in einem separaten Array haben):

GET    api/Module_name
GET    api/Module_name/42
POST   api/Module_name
PUT    api/Module_name/42
DELETE api/Module_name/42
GET    api/Module_name/42/comments
GET    api/Module_name/42/comments/13
POST   api/Module_name/42/comments
PUT    api/Module_name/42/comments/13
DELETE api/Module_name/42/comments/13

Speicherort der Routenstrukturdateien


Module im CleverStyle Framework speichern alles im Modulordner (im Gegensatz zu Frameworks, bei denen sich alle Ansichten in einem Ordner befinden, alle Controller in einem anderen, alle Modelle im dritten, alle Routen in einer Datei usw.), um die Wartung zu vereinfachen.

Abhängig von der Art der Anforderung werden verschiedene Konfigurationen im JSON-Format verwendet:

  • für reguläre Seiten modules/Module_name/index.json
  • für Administrationsseiten modules/Module_name/admin/index.json
  • für API modules/Module_name/api/index.json
  • für CLI modules/Module_name/cli/index.json

In denselben Ordnern befinden sich Routehandler.

Routing-Typen


Es gibt zwei Arten von Routing im CleverStyle Framework: dateibasiert (früher häufig verwendet) und controller-basiert (heute häufiger verwendet).

Nehmen Sie die Seite Blogs/latest_postsund die endgültige Route aus dem obigen Beispiel ['index', 'latest_posts'].

Bei dateibasiertem Routing werden die folgenden Dateien in der angegebenen Reihenfolge verbunden:

modules/Blogs/index.php
modules/Blogs/latest_posts.php

Wenn controller-basiertes Routing verwendet wird, muss eine Klasse cs\modules\Blogs\Controller(Datei modules/Blogs/Controller.php) mit den folgenden öffentlichen statischen Methoden vorhanden sein:

cs\modules\Blogs\Controller::index($Request, $Response) : mixed
cs\modules\Blogs\Controller::latest_posts($Request, $Response) : mixed

Es ist wichtig, dass alle Dateien / Methoden mit Ausnahme der letzten weggelassen werden können. und dies wird nicht zu einem Fehler führen.

Nehmen wir nun ein komplexeres Beispiel, eine Abfrage GET api/Module_name/items/42/comments.

Erstens ist für die API- und CLI-Anforderungen neben dem Pfad auch die HTTP-Methode von Bedeutung.
Zweitens wird hier ein Unterordner verwendet api.

Bei dateibasiertem Routing werden die folgenden Dateien in der angegebenen Reihenfolge verbunden:

modules/Module_name/api/index.php
modules/Module_name/api/index.get.php
modules/Module_name/api/items.php
modules/Module_name/api/items.get.php
modules/Module_name/api/items/comments.php
modules/Module_name/api/items/comments.get.php

Wenn controller-basiertes Routing verwendet wird, muss eine Klasse cs\modules\Blogs\api\Controller(Datei modules/Blogs/api/Controller.php) mit den folgenden öffentlichen statischen Methoden vorhanden sein:

cs\modules\Blogs\api\Controller::index($Request, $Response) : mixed
cs\modules\Blogs\api\Controller::index_get($Request, $Response) : mixed
cs\modules\Blogs\api\Controller::items($Request, $Response) : mixed
cs\modules\Blogs\api\Controller::items_get($Request, $Response) : mixed
cs\modules\Blogs\api\Controller::items_comments($Request, $Response) : mixed
cs\modules\Blogs\api\Controller::items_comments_get($Request, $Response) : mixed

In diesem Fall mindestens eine der letzten beiden Dateien / Controller muss existieren.

Wie Sie sehen, verwenden die API- und CLI-Anforderungen eine explizite Trennung des Anforderungsverarbeitungscodes mit verschiedenen HTTP-Methoden, während dies für reguläre Seiten und Verwaltungsseiten nicht berücksichtigt wird.

Argumente in Controllern und Rückgabewert


$Requestund $Responsenichts als Exemplare cs\Requestund cs\Response.

In einfachen Fällen reicht der Rückgabewert aus, um den Inhalt festzulegen. Unter der Haube für API-Anforderungen wird der Rückgabewert an cs\Page::json()und für den Rest der Anforderungen an übergeben cs\Page::content().

public static function items_comments_get () {
    return [];
}
// полностью аналогично
public static function items_comments_get () {
    Page::instance->json([]);
}

Ungültige HTTP-Methodenhandler


Es kann vorkommen, dass es keinen HTTP-Handler für die vom Benutzer angeforderte Methode gibt. In diesem Fall gibt es mehrere Szenarien für die Entwicklung von Ereignissen.

API: Wenn es cs\modules\Blogs\api\Controller::items_comments()keine cs\modules\Blogs\api\Controller::items_comments_get()(oder ähnliche) Dateien gibt, dann:

  • Zunächst wird die Existenz des Methoden-Handlers überprüft OPTIONS. Wenn es einen gibt, entscheidet er, was damit zu tun ist

  • Wenn OPTIONSkein Methodenhandler vorhanden ist, wird eine automatisch generierte Liste der vorhandenen Methoden im Header gesendet. Allow(Wenn die aufgerufene Methode von abweicht OPTIONS, wird der Statuscode zusätzlich in geändert. 501 Not Implemented)

CLI: Ähnlich wie bei der API, jedoch anstelle einer OPTIONSspeziellen Methode CLIund anstelle des Headers werden die Allowverfügbaren Methoden in der Konsole angezeigt (wenn sich die aufgerufene Methode von unterscheidet CLI, wird auch der Beendigungsstatus in 245( 501 % 256) geändert ).

Verwenden Sie Ihr eigenes Routing-System


Wenn Ihnen das Routing-Gerät im Framework aus irgendeinem Grund nicht gefällt, können Sie in jedem einzelnen Modul nur eine index.phpDatei erstellen und den Router nach Ihren Wünschen damit verbinden.

Da index.phpController und Struktur nicht erforderlich sind index.json, umgehen Sie den größten Teil des Routingsystems.

Zugangsrechte


Für jede Ebene der Route werden Zugriffsrechte überprüft. Zugriffsrechte im Framework haben zwei Schlüsselparameter: group und label.

Bei der Überprüfung der Zugriffsrechte auf eine Seite wird als Gruppe der Name des Moduls mit einem optionalen Präfix für Verwaltungsseiten und API verwendet, und der Routenpfad (ohne Präfix index) wird als Bezeichnung verwendet .

Beispielsweise werden die api/Module_name/items/commentsRechte des Benutzers für Berechtigungen für die Seite überprüft (durch ein Leerzeichen getrennt group label):

api/Module_name index
api/Module_name items
api/Module_name items/comments

Wenn der Benutzer auf einer bestimmten Ebene keinen Zugriff hat, schlägt die Verarbeitung fehl 403 Forbiddenund die Handler der vorherigen Ebenen werden nicht ausgeführt, da die Zugriffsrechte in der Phase der endgültigen Routenbildung festgelegt werden , bevor die Handler gestartet werden.

Am Ende


Die Implementierung der Abfrageverarbeitung im CleverStyle Framework ist recht leistungsfähig und flexibel und gleichzeitig deklarativ.

Der Artikel beschreibt die wichtigsten Phasen der Anforderungsverarbeitung aus Sicht des Routingsystems und seines Interesses für den Entwickler. Wenn Sie sich jedoch mit den Nuancen befassen, gibt es noch etwas zu lernen.

Ich hoffe, dieser Leitfaden reicht aus, um sich nicht zu verirren. Jetzt sollte klar sein, warum Sie nicht einmal in die Konfiguration schauen müssen, um festzustellen, welcher Code als Antwort auf eine bestimmte Anforderung aufgerufen wurde. Es reicht aus, die Art des von der Anwesenheit Controller.phpim Zielordner verwendeten Routings zu bestimmen und die entsprechende Datei zu öffnen.

Die aktuelle Version des Frameworks zum Zeitpunkt des Schreibens von Artikel 5.29, Änderungen in neueren Versionen sind möglich, folgen Sie den Versionshinweisen.

"GitHub-Repository
» Framework-Dokumentation

Konstruktive Kommentare sind grundsätzlich willkommen.

Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte komm rein .

Welchen Ansatz bevorzugen Sie?

  • 23,5% aussagekräftig 4
  • 76,4% Imperativ 13

Jetzt auch beliebt: