StuckMVC
Scratch für wichtige ToDos, Notizen, Gedankengänge, Entscheidungen, ...
-
RequestHandler
-
Dispatching-Flag
-
Flag, die Application::run() mitteilt, das vor Routing RequestHandler invoked werden soll.
Aktuell wird der Handler immer gestartet, wenn !empty($_REQUEST). Aber alleine xdebug unterwandert das ja schon. -
Ausnahmen in $_REQUEST erlauben?
-> Nee, theoretisch zwar endliche Menge, aber hochgradig variabel und u.U. sehr lang usw... Insgesamt einfach kein guter Ansatz. -
Überlegung:
Wäre es denn so schlimm, wenn der RequestHandler auch mal unnötiger weiße invoked wird? Er canceled ja sowieso, wenn process() nicht alle mandatory fields kriegt ([cl, fnc]).
-> Was kommt denn alles in Frage außer Debugging-Tools, was default Request-Variablen setzt?
Eigentlich gar nicht soo viel und wenn dann so gut wie immer in der Dev-Environment, da kann man das ja im Grunde auch verkraften
-> Im Zweifelsfall einfach eine extendable ignore list für gängigen Kram wie Zend/xDebug/... mitgeben?
-> Neee, hier wird nicht gepfuscht. Das Teil soll ja nicht gut sondern genial werden!
Ordentliche Lösung erarbeiten! -
-> Überlegung:
Was, wenn tatsächlich mal API für Requests von außen angedacht?
Also wenn käme schonmal eh nur REST-Pattern in Frage.
Das wäre im Grunde verträglich, da aktueller RequestHandler nur für interne Requests wie Login gedacht.
Aber ein Flag wäre da dann trotzdem gut, damit nicht wieder gleiche Probleme aufkommen, falls wirklich API.
-
Flag, die Application::run() mitteilt, das vor Routing RequestHandler invoked werden soll.
Hidden Fields festlegen-
'cl' (mandatory)
-> Für target controller identifier (gemäß ControllerMap) -
'fnc' (mandatory)
-> Für target function innerhalb des Controllers -> Art der Zielfunktion bestimmen: Reflection/try-catch? -
'ref' ?
-> Nein, überflüssig, denn Ref ist IMMER aktuelle Seite (bei internal requests)
-> bereits abrufbar (Router::getCurrentPage())
-
Überlegung
Werden internal requests IMMER die Funktion eines Controllers als target haben?
-> Was, wenn nicht?
Aktuell basiert alles auf Controller, egal ob Core/Pages/Widgets/Module.
Aber wenn User kleine Snippets einbauen will?
Dafür gibts ja eigentlich Widgets und Module...
Aber Cronjob-Skripte? Nein, lieber richtig supporten -
Wenn dann noch etwas nicht abgedeckt ist, muss es ziemlich advanced sein
-> D.h. User ist erfahren und wird demnach schon wissen (in Doku schreiben!), das unter cl im Grunde auch outscoped Klassen ansprechbar, solange FQN-Angabe und autoloadable/required -
Response-Anweisungen?
-> z.B. login -> success? (y/n)
-> keine Präsentationslogik -> Job der Target-Funktion
-> Außerdem Großteil aller Fälle sowieso abgedeckt durch Möglichkeit, target zu ändern (bereits implementiert) -
Überlegung
-> Änderung target aktuell durch return der Funktion
Vielleicht doch besser für Response extra Request-Feld implementieren? -> Einheitlicher / nicht komplexer / logischer / zusätzliche response Felder einfacher möglich / scope
-> Definitiv muchos Vorteile, nixi dixi Nachteile außer mehr Arbeit zu entwickeln -> implementieren!
-
'cl' (mandatory)
-
AllowAutoload? Scopes?
-> Welche Funktionstypen zu unterstützen macht überhaupt Sinn?
-> Request vulnerabilities beachten -
Am Ende Handler nochmal in Ruhe komplett überarbeiten
-> RequestHandling ist A und O
-> Prioritäten-Ranking:
(i) Sicherheit
(ii) Usability / Anwenderfreundlichkeit
(iii) Geschwindigkeit
(iv) State-of-the-Art
(v) Präferenzen
-
Dispatching-Flag
-
Backend
- Zurückgesetellt, bis das neue Model-Pattern fertig implementiert ist
-
Facts
-
Implementation
- Daten, die ab der Installation konstant sind, ablegen.
-
Autoloader
- Autoloader stammt noch aus Zeit, bevor Facts-Klasse eingeführt -> zusammenwirken optimieren
-
Implementation
-
Caching-System
-
CacheAdapter
- Aktuelle Cache-Klassen nicht PSR4-konform (TTL usw)
-
Angedachte Typen für das neue CachingSystem (in RFC prüfen, ob so noch PSR4-konform)
-
CacheGroup
-> Container für CacheItem -
CacheItem
-> kleinste Cache-Informationseinheit einer CacheGroup
-
CacheGroup
-
CacheAdapter
-
Model
-
Neue Logik
-
Das aktuelle eigene Pattern ist zwar an und für sich nice, aber da das ganze hier auch als Ref taugen soll, doch lieber auf super nice umbauen
Entscheidung: Kombination aus... - Angepasste "Active Record" Variante #2 ("Active" nicht zuletzt erzeugtes Objekt, sondern wörtlich, also eingeloggter User, Kategorie/Contentpage wo man gerade ist... ). Für Models wo das kein Sinn macht (Log-Records oder sowas) eben sinnvolle Alternativen, z.B. eben zuletzt geladener Record bzw zuletzt erzeugtes Objekt o.ä.
- Die vorhandene LazyLoad-Logik mit __get() und __set() aus alter Model-Architektur
- Die schon teilweise umgesetzte QueryBuilderFactory für alles, was die Models schlecht/nicht leisten können
-
-
Konzeption AR-Variante #2:
Active Record Pattern wackelig. Wenn neuer User erzeugt ohne anderen $name wird und ist nicht der eingeloggte ("active") User ("Record"), wird TPL-Var überschrieben.
=> Objekt mit aktivem User/Record in Session serialisieren. Kein Singleton oder so.
=> Aller erstes Objekt (je Model) nach Erzeugung auch in Session serialisieren
=> Alle weiteren Objekte werden daraus geklont (unserialize) aber ohne ID,Col-Values,isLoaded,... ("unloaded")
=> Wird User eingeloggt (aktiver Record [nach meiner Def] entsteht), wird DIESES Objekt in Session serialisiert (überschreibt alten, falls vorhanden) und dient fortan als Vorlage, was erstmal kein Unterschied macht.
=> Sobald ein aktiver Rekord existiert kann Model::getActive() (oder so) genutzt werden. Die unserialisiert dann (wie der normale Konstruktor) das "aktive" Objekt aus der Session, ABER DIESMAL MIT ALLEN WERTEN, also im "geladenen" Zustand und gibt es zurück.
=> Bei logout o.ä. sollte dann aus Sicherheitsgründen das Obj in Session auch "unloaded" werden, auch wenn es reichen würde, das static Property [zB User->isActive] auf false zu setzen. (Session hijacking etc)
--> Bei Models, für die Definition eines "aktives" Objekts eher unlogisch/sinnlos (Log-Records o.ä.), geben da dann eben iwas halbwegs sinnvolles... der letzte oder sowas Richtung setActive().
Ooooder:
Sogar Model-Subtyp für solche Daten ohne AR-Pattern.... (bzw. andersrum, weil die mit haben dann ja die zusätzlichen Member, also ActiveRecordModel extends Model)... mal schauen dann.
-
Neue Logik
-
Umstieg auf PDO
- Besser erstmal vom Umbau auf neues Pattern erstmal außen vor lassen, wenig Erfahrung
- Ist Transact wirklich so wichtig zu haben? Naja, mindestens nice 2 have. Schadet nicht -> auf PDO umbauen!
-
ErrorHandler
- Basiert noch auf alten Ansatz für ExceptionHandling
- Neu umsetzen!
-
Logger
- Stand noch aus der Zeit vor The Great Rebuild #2
-
Neu umsetzen!
-> zum Singleton-Register hinzufügen
-
Neuer Aufbau des Routing-Tables (Beispieleinträge) (already implemented!)
$aRoutingMap = [ '' => [ 'template' => 'application/views/pages/home/index.tpl', 'title' => 'Home', 'description' => 'Auf dieser Seite finden Sie alles, was (mein) Herz begehrt', 'keywords' => 'stuck, stuck1a, s1a, coding, programmierung, preview, mvc' ], '/' => [ 'template' => 'application/views/pages/home/index.tpl', 'title' => 'Home', 'description' => 'Auf dieser Seite finden Sie alles, was (mein) Herz begehrt', 'keywords' => 'stuck, stuck1a, s1a, coding, programmierung, preview, mvc' ], 'music' => [ 'template' => 'application/views/pages/music/index.tpl', 'title' => 'Musik', 'description' => 'Alles rund zum Thema Musik', 'keywords' => 'musik, sammlung, playlist' ], 'music/byartist' => [ 'template' => 'application/views/pages/music/byartist.tpl', 'title' => 'Musik', 'description' => 'Alles rund zum Thema Musik', 'keywords' => 'musik, sammlung, playlist' ] // ... ];
-
Zusätzlich bedenken bzw im Hinterkopf behalten
-
Programmierer wollen eventuell zusätzliche Felder ("custom seo field") hinzufügen. Immerhin ist addSeoRoutes()
usw ja public, damit das ganze Teil maximal angepasst werden kann, wenn ein erfahrener Coder es nutzt. Die
Getter und sonstiger Accessors sollten damit also klarkommen.
-> Eigener Typ anlegen, dann kann auch wieder in der getSEOData() unterschieden werden, ob ein ungültiges SEO-Feld angefordert wurde oder der Routing-Eintrag korrumpiert ist (z.B. leeres 'template' Feld bei einer content page oder anderen routable objekten, die definitiv immer einen Wert darin haben müssen). Dann könnte man z.B. auch versuchen, diesen einmal neu zu erzeugen o.ä. (vll hat ja jmd manuell in Datenbank/FileCache rumgepfuscht), bevor Exceptions geworfen werden. -
Im jeweiligen Controller bzw. PageController sollten dann entweder entsprechende Template-Variable assigned
werden, die dann aber nur scope auf das jeweilige Template haben.
Oder man kann vom Template aus irgendwie über den Controller die Properties abrufen
(Template Getter oder Plugin-Funktion) -
Die Duplikat-Einträge (die SEO-Link-Varianten von ein und der selben content page abdecken) sollten erstmal
unabhängig voneinander bleiben. Im CMS-Backend sollen diese dann aber verknüpfbar sein, so dass man z.B. für
die default Varianten von home ("/", "", ...) nur einmal die SEO-Daten unter dem Haupteintrag (z.B. "home")
pflegen muss und diese beim speichern auch für alle verknüpften Routen ("symlinked routes") eingetragen werden.
So kann man (wenn man will) trotzdem noch z.B. unterschiedliche Titel angeben, je nachdem über welchen
SEO-Link man auf einer content page gelandet ist, ohne das deswegen gleich alle Varianten manuell
mitgepflegt werden müssen (Jetzt mal really: das würde doch eh niemand machen), nur weil man mal an ein oder
zwei Routen-Varianten abweichende SEO-Daten haben will. Merke: SEO muss möglichst viel sinnvollen default
content haben, da es in der Praxis sehr häufig stark vernachlässigt wird.
Wir wollen aber trotzdem gute Rankings :)
-
Programmierer wollen eventuell zusätzliche Felder ("custom seo field") hinzufügen. Immerhin ist addSeoRoutes()
usw ja public, damit das ganze Teil maximal angepasst werden kann, wenn ein erfahrener Coder es nutzt. Die
Getter und sonstiger Accessors sollten damit also klarkommen.
-
Breadcrumb-Widget
- funktioniert noch nicht für die beiden Root-Extremfälle "" und "/"
-
Suchfunktion
- Controller/Widget/TPL ? -> Erst Widgets fertig machen, dann schauen, womit es am besten geht
-
Der eine Punkt, der mir gerade nicht einfällt...
Kinder anbrüllen, die auf dem Gehweg skaten?
-Ah ne, des war ja gestern schon Programm (Spaßfaktor war etwa 8 von 10, weil ein böser Bub dabei war)
Shit, voll verkapselt... Naja. -
Profiler
- noch ordentlich ausprogrammieren
-
Sinnvolle Smarty-Pluginfilter
- Alle HTML-Kommentare entfernen (Output)
-
HTML-Tags immer schließen (Pre/Post/Output/Compiler)
-> Also z.B. ˂br˃ automatisch zu ˂br /˃ umschreiben -> Kompatibilität zu XHTML
-> ermöglicht Interoperabilität mit XHTML-Webapplikationen (gibts sowas überhaupt noch?)
-> nochmal darüber nachdenken wenn es soweit ist -
Solche potenziellen Overheads in Backend de-/aktivierbar
Deaktivierte Filter sollen dann gar nicht erst von Smarty registriert werden
-
Sinnvolle Widgets
- Timeline
- Galerie (Component oder Widget? Nee, hat viel Variationsmöglichkeiten, schon eher Widget...)
-
Pagination
-> auch sinnvolle Core-Logik überlegen, wie solche "Blog"-artigen ContentPages am besten gemanagt werden -
Notificator (Für Ausgabe von Zustandsnachrichten/Fehlermeldungen usw)
-> Überall wo es eingebunden wird, können Status-Messages (Infos/Errors/...) ausgegeben werden. Wenn Template-Messages gesetzt werden, kann man dann zB angeben, an welcher dieser Stellen die Nachricht ausgegeben werden kann. Je nach Message-Typ passt sich das Template an (z.B. das Glyph auf Fehler setzen usw)
-> Alternativ kann man als Location auch eine beliebige ID eines HTML-Elements setzen, dann wird es als Pop-Up Nachricht darunter/darüber/whatever angegeben. Dabei kann auch noch Korrektur top/left in px/% und ein z-index angegeben werden. Das Template wird dann als absoluter Container eingefügt und ausgerichtet.
-> Was wenn display=none o.ä.? Fallback? Error?
-
Support für externe Medienquellen?
-
Vorallem Laien greifen schnell auf teuren Hoster-inklusiven Webspace zurück
-> Einfache Möglichkeit implementieren, um Mediendateien gescheit mit Smarty facaden zu können, so dass es praktisch möglichst egal ist, woher der Stoff für die Ausgabe kommt
-> Denkbare Lösungen:
Automatisch erkennen (Protokoll/Link)
Angabe (Param/Flag)
Getrennte Funktionen (Pluginfunktion/RessourceHandler)
-> Wählbar?
-
Überlegung:
Composite Wrapper u.a. mit der vorhandenen Pluginfunktion "getImagesPath" -
Wie es die OXID-Dudes gelöst haben:
Die bauen Component-Klassen teils on-the-fly, assignen sie global in Smarty mit Support-Funktionen (z.B. $oViewConf->getImageLink($sFilename)
-> Hat natürlich schon was... für Anwender... Aber wenn der Anwender ein Coder ist, dann ist das halt einfach ähnlich mau wie sich in ihre Magic-Flut einzuarbeiten
-
Vorallem Laien greifen schnell auf teuren Hoster-inklusiven Webspace zurück
- Namenskonventionen und PHPDocBlock's normalisieren (PSR)
-
ClassicTheme
- Content-Bereich (bzw. Base-Bereich) soll 2-3 Zeilen hohes margin-bottom erhalten (überschreibbar)
-
Typ "ContentPage" o.ä. einführen
-> Enthält dann alle SEO-Daten und noch mehr anstatt den aktuellen Arrays als Items von der routing map -> z.B. dann noch properties wie oParent/aChilds/iDepth usw, vereinfacht Umgang für content pages sowohl für mich (siehe Breadcrumbs) als auch für den User. Und ist auch insgesamt einfach wichtig und sinnvoll.
-
Auftrennen des Projekts in Package Stuck1A/StuckMVC und Subpackage Stuck1A/ClassicTheme, welches
in der composer.json des Hauptpackages required wird, so das es automatisch mitinstalliert wird.
-> Was für ein pures Theme-Package schon zu spezifisch ist (Views/Components/Widgets/Plugins/Controller) nochmal abtrennen zu Package Stuck1A/StucksSeiten und zur Beispielwebsite ausbauen.
Bedenken: ClassicTheme = Basis-Theme = required = integraler Bestandteil + etwas an Demodaten soll das Setup schon auch anbieten können (aber neutrale...)
Sinnvollerweise könnte das auch gleich die offizielle Seite für das Framework werden (Name/Domain evtl anpassen) zwecks Bezug und Online-Doku.
Dann taugt es auch mal als CV-Ref.
- Implementation eines vorgeschalteten Setup-Prozesses, welcher alle individuellen Configs abfragt und im Experten-Modus auch die Defaultwerte von technischen Konfigs wie die mapping-section verändern lässt. Erzeugt auf Basis der Angaben die Ordnerstruktur, kopiert alle Files zu mitgelieferten Subpackages (Themes etc) an die vorgesehenen Orte und schreibt auch die Inhalte von Dateien wie Apache-Konfigurationsfiles u.ä. entsprechend der angegebenen Konfiguration. Erzeugt die initiale Struktur in der Datenbank bzw. die Pseudotables (letzteres eig nicht nötig, da Model.php das im Zweifelsfall im Request erledigt, falls da was fehlt), je nachdem ob man eine Datenbank anbindet oder nicht. Prüft auch, ob nötige PHP-Extensions und Apache-Module aktiv sind und gibt ggf. Hinweise auf deren Aktivierung, falls nötig. Nach erfolgreicher Validierung der Installation wird das Setup-Verzeichnis automatisch gelöscht, falls möglich. Ansonsten wird ein Hinweis ausgegeben, dass man das jetzt löschen sollte, bevor man loslegt mit entwickeln/einrichten. Es werden abschließend die beiden Root-Links angegeben, mit denen man auf die Startseiten von FrontEnd und BackEnd gelangt.
-
LÖSUNGWEG 1 - Widget-Calls sammeln um am Ende erst dispatchen, wenn Template gerendert
- Die registerController() anpassen, damit sie auch Controller in widgets-Verzeichnis(sen) registriert.
- In Config ein Array-Property "parsedWidgetCalls" o.ä. anlegen, in dem die erfassten Calls zu Widget- Controllern als fully qualified namespace (dadurch muss der Dispatcher den Widget-Namespace nicht selbst kennen) hinterlegt werden.
- Smarty-Pluginfunktion "include_widget" anlegen, die den Controller-Identifier und alle Parameter für den WidgetController-Konstruktor als Parameter erwartet. Identifier und Parameterliste werden dem Setter von parsedWidgetCalls ("addParsedWidgetCall()") übergeben.
- Der Setter holt sich den Klassennamen vom Controller über getRegisteredController(['identifier']) und um den Widget-Namespace (allmählich wird es Zeit für eine "getNamespace()"...) ergänzt. Das Ganze wird dann zusammen mit der Parameterliste als neues Arrayitem im Property abgelegt.
- In der index.php werden nach den Models (auf die selbe Art wie diese) alle WidgetController dispatched, in der Reihenfolge, in der sie beim parsen von Smarty erfasst wurden.
-
LÖSUNGSWEG 2 - Widgets direkt im Plugin dispatchen
-
Dafür alternative Pluginfunktion anlegen, die den Aufruf nicht einträgt, sondern einfach direkt den entpr.
Widget-Controller dispatched und die Routine zum dispatchen aus der index.php auskommentieren.
Dann könnte der Controller des Widgets direkt an der eingebundenen Stelle "wirken"
(Kann er? Compile-Time... Nix Browser rendern, nix DOM... oder doch? Sollte Unterschied machen, zumindest die Platzierung im DOM. Aber ist DOM dann schon aufgebaut? Checken. -
Fragestellungen
- Wie verhält sich das Ganze im Zusammenspiel mit Smarty?
- Ist der DOM wie anzunehmen tatsächlich in beiden Fällen praktisch im gleichen Zustand?
- Welche Vor-/Nachteile sich ergeben sich aus den Methoden?
- Wie viel Varianz in der Widget-Struktur verkraften die jeweiligen Methoden?
-
Resultat
-> Widgets definitiv on-the-fly mitrendern, alles andere macht mehr Probleme als es löst!
-
Dafür alternative Pluginfunktion anlegen, die den Aufruf nicht einträgt, sondern einfach direkt den entpr.
Widget-Controller dispatched und die Routine zum dispatchen aus der index.php auskommentieren.