.MB: Route strings zu Klassen Konstruktor

moin,

ich habe eine Route Klasse erstellt die dann die url nimmt und daraus strings generiert. z.B. website/main/index/1 zu main, index, [ '1' ]

in php gehts ziehmlich einfach aus diesen strings ne Klasse zu generieren

new $controller( $method, $params );

In TypeScript habe ich es mit zwei simultan iterierenden Objekten Strings und Constructors gelöst

for( let i : number = 0; i < list.name.length; i++ ) {
  if( list.name[ i ] === Route.controller )
    new list.controller[ i ]( Route.method, Route.params );
}

weil man zweimal iterieren muss um den gewünschten Konstruktor zu bekommen ist das denke ich mal nicht sehr performant. Gibts noch andere Lösungen?

vlg MB

  1. Ich würde mich hier an das Prinzip "Convention before Configuration" halten. Sprich: Wenn Du in der Route einen Controller "Haschmich" findest, würde ich zwingend eine Klasse "Haschmich" oder "HaschmichController" instanziieren. Diese Klasse kann ggf. in dem einen oder anderen Namespace zu finden sein (die müsstest Du durchsuchen und dafür müsstest Du ggf. eine Liste erlaubter Controller-Namespaces führen).

    In PHP müsste diese Logik auch irgendwie mit dem Class-Loader zusammenarbeiten, d.h. du bräuchtest im Classloader eine Funktion "Gibt's diese Klasse" statt blindlings einen new $controllerWithNamespace zu machen und dann ins Errorhandling zu rasseln.

    Den Namen der Methode (oder Action) würde ich bei der Klassenkonstruktion außen vor lassen. Statt dessen würde ich dynamisch eine Methode aufrufen, die der Action entspricht.

    In JavaScript/Typescript müssest Du Dir ebenfalls Gedanken zum Namespace machen; da weiß ich nicht wie Typescript funktioniert. Aber auch da würde ich den Namen der Action nicht an den Konstruktor geben, sondern prüfen, ob das Controller-Objekt ein Property mit dem Namen der Action hat und ob es eine Funktion ist. Wenn ja, ruf sie auf.

    Als weitere Sicherheitsebene kannst Du noch festlegen, dass der Name einer Methode, die eine Action implementiert, mit dem Wort "Action" beginnen oder enden muss. Dieses "Action" fügst Du dem Action-Namen aus dem Routing hinzu. Damit stellst Du sicher, dass NUR Actions über das Routing aufgerufen werden können. Ähnliches kannst Du mit den Controllern machen, du legst die Namespaces fest in denen Controller stehen dürfen und definierst für Dich, in diesen Namespaces NUR Controller unterzubringen. Und Du kannst zwingend ein "Controller" an den Controllernamen anhängen. Viele Möglichkeiten :)

    Wenn Du es unbedingt konfigurativ lösen willst, würde ich nicht Arrays durchsuchen. Sowohl PHP als auch Javascript kennen assoziative Arrays, d.h. wenn Du einen Controller Haschmich mit den Actions Schnapp und Fang haben willst, könntest Du es in Typescript so konfigurieren:

    var controllerMap = { 
       Haschmich: {
          Schnapp: { controller: HaschmichController, method: 'schnappAction' },
          Fang:    { controller: HaschmichController, method: 'fangAction' }
       }
    }
    

    In PHP geht das ähnlich:

    $controllerMap = ARRAY('Haschmich' => 
        ARRAY('Schnapp' => new RouteHandler('HaschmichController', 'schnappAction'),
              'Fang'    => new RouteHandler('FangController', 'fangAction')));
    

    Und dann kannst Du es in Javascript so aufrufen (minimale Existenzprüfungen, kein Errorhandling):

    routeHandler = (controllerMap[controllerName] && controllerMap[controllerName][actionName]);
    if (routeHandler && routeHandler.controller && typeof routeHandler.controller === "function") {
       let controller = new routeHandler.controller();
       if (typeof controller[routeHandler.method] === "function")
          controller[routeHandler.method](routeParameters);
    }
    

    PHP ist ähnlich, da musst Du mit isset arbeiten.

    Eine konfigurative Lösung hat den Nachteil, dass Du pro Web-Request nur eine einzige Route verarbeiten wirst, aber die Steuertabelle für alle aufbauen musst. Das kostet Zeit (und es kostet noch mehr Zeit, lieber pl, das aus einer Datei einzulesen). Die Konventionslösung hat den Vorteil, dass Du bei korrekt existierenden Controllerklassen und Actionmethoden zwei schnelle Dictionary-Zugriffe hast und dann am Ziel bist.

    Wenn Du allerdings die Möglichkeit hast, die Steuertabelle nur einmal aufzubauen und dann requestübergreifend zu speichern, relativiert sich dieser Nachteil.

    Rolf

    1. Hallo Rolf,

      ja mit phphab ichs schon gemacht und es ist da sehr einfach, wenn ich dich richtig verstehe, versteht sich ;-). TypeScript eine interessante herangehensweise um ableitend mit Controllern arbeiten zu können kann man hier unter Rubrik Difference between the static and instance sides of classes finden. Da habe ich dese herengehensweise für das Routing System her.

      Das in meinem Beispiel war ein assoziatives Array und ich habs exakt so gemacht wie du empfolen hast. Also habe ich nix falsch gemacht ^^.

      vlg MB

      1. Das in meinem Beispiel war ein assoziatives Array und ich habs exakt so gemacht wie du empfolen hast.

        for( let i : number = 0; i < list.name.length; i++ ) {
          if( list.name[ i ] === Route.controller )
            new list.controller[ i ]( Route.method, Route.params );
        }
        

        Sieht mir nach einem Objekt list mit einem name Property aus, das ein Array ist, in dem über numerische Indexe Controllernamen gespeichert sind und das sequenziell durchlaufen wird. Dazu gibt's dann ein zweites Array im controller Property, worin die Klassenkonstruktoren stehen, die den Controller aus der Route implementieren.

        Ein assoziatives Array verwendet direkt Controllernamen als Schlüssel und erspart Dir die sequenzielle Suche. Vergleiche meine Code und Deinen, bei mir wirst Du keine Schleife finden. Nur direkte Propertyzugriffe (wohinter sind die Dictionaries der Property-Implementierung von Javascript verbergen).

        Also habe ich nix falsch gemacht ^^.

        Hat keiner behauptet. Aber ganz anders, als ich es vorgeschlagen habe. Was natürlich dein völliges Recht ist. Nur solltest Du dann nicht meinen, ein assoziatives Array verwendet zu haben. Das wäre ein alternativer Fakt... ;-)

        Rolf

        1. Tach!

          Ein assoziatives Array verwendet direkt Controllernamen als Schlüssel und erspart Dir die sequenzielle Suche.

          Ein assoziatives Array in Javascript ist in Wirklichkeit ein einfaches Objekt. Selbst wenn man eine Instanz von Array dafür verwendet, nimmt man dann nur die allgemeinen Eigenschaften die allen Objekten gemein sind. Man sollte in dem Kontext den Begriff "assoziatives Array" meiden, weil er irreführend sein kann.

          dedlfix.

          1. Ein assoziatives Array in Javascript ist in Wirklichkeit ein einfaches Objekt.

            Ok, das hätte ich klarer darstellen können. Es gibt einerseits die abstrakte Datenstruktur "assoziatives Array", die auch Dictionary, Symboltabelle oder Map genannt wird. Die englische Wikipedia stellt diese Begriffe als Synonyme dar.

            Andererseits gibt es die Implementierungen. In PHP ist es die ARRAY-Funktion, die ein Konstrukt erzeugt das numerisch und alphanumerisch indizierbar ist. In JavaScript sind es die Objekte, deren Properties in einem Dictionary verwaltet werden und daher direkt als assoziatives Array nutzbar sind. In anderen Sprachen findet man passende Implementierungen in der Runtime-Library.

            Und drittens gibt es numerisch indizierte Arrays, die man sequenziell durchsucht und mit "assoziatives Array" nicht in Verbindung bringen sollte. Auch dann nicht, wenn JavaScript und PHP so umständlich sind und ein Array mit numerischen Indizes genauso assoziativ speichern wie eins mit alphanumerischen Indizes.

            Rolf

        2. ieht mir nach einem Objekt list mit einem name Property aus, das ein Array ist, in dem über numerische Indexe Controllernamen gespeichert sind und das sequenziell durchlaufen wird. Dazu gibt's dann ein zweites Array im controller Property, worin die Klassenkonstruktoren stehen, die den Controller aus der Route implementieren.

          Sorry ja natürlich, ich wollte nur ausdrücken das ich zwei nummerische Arrays in ein assoziatives gepflanzt habe und die dann simultan durch rattern lasse. Dein Code inspiriert mich. Danke dafür :)

          vlg MB

          1. Na gut. Im Fall des Objekts in list würde ich aber nicht von einem assoziativen Array sprechen, weil da keine beliebigen Keys verwendet werden. Es ist ein ganz normales Objekt mit Properties.

            Rolf