| //Einen Webshop für einen Internetauftritt// | \\ \\ \\ \\ ====== Teil 1 ====== ====== 1Aufgabenstellung ====== ===== 1.1Ausgangslage ===== Der Kunde möchte einen Webauftritt mit dem er Merchandising-Artikel verkaufen kann. In der ersten Phase wird ein einfacher Webshop ohne Designelemente programmiert. Nachdem der Webshop fertiggestellt wurde wird der eigentliche Webauftritt erstellt. \\ \\ Die Bedienung soll möglichst einfach sein. \\ \\ Seitens der Rafisa möchten wir, dass die Software modular aufgebaut ist. \\ \\ Als Vorarbeit wird eine Test-Datenbank mit Artikel-Daten aufgebaut die dann, bei der IPA, in die produktive Datenbank importiert werden. Diese dient um den Umgang mit Bilddaten (BLOB) aus der SQL-Datenbank zu testen. \\ \\ Die IPA soll eine folgende Funktionalität liefern: • Einen Warenkorb bei dem man eine Bestellung abgeben kann. • Einen Warenkorb bei dem man Artikel aufnehmen, löschen und deren Bestellungsanzahl ändern kann. • Eine Artikelliste die maximal 10 Artikel pro Blatt anzeigt. • Eine Artikelliste die man Blättern kann. • Eine Artikelliste bei der man einen Artikel in einer Detailansicht einsehen kann. • Speicherung einer Bestellung. \\ \\ Folgende Arbeiten gehören nicht zur IPA und werden bei dessen Abschluss weiterentwickelt: • Online-Bezahlungsmöglichkeit (Paypal). • Artikelverwaltung (neue Artikel erfassen, Statusänderung, Löschung, etc...). • Bestellungsverwaltung (Bestellungen stornieren, Rechnungsadresse/Lieferadresse ändern, Artikelpositionen löschen, ändern, etc...). • Design des Webauftrittes nach Kundenvorgabe. • Seitenaufbau des Webauftrittes (Home, Geschichte, Webshop, etc...) • Bestellungsbestätigung per E-Mail \\ \\ ===== 1.2Detaillierte Aufgabenstellung ===== Der erste Schritt ist das Sicherstellen, dass Sie die notwendige Software und Hardware installiert und konfiguriert haben, damit Sie professionell damit entwickeln können. Stellen Sie sicher, dass das Endprodukt dem objektorientierten Paradigma entspricht. \\ \\ Source Control: Damit eine Kontrolle und Nachverfolgung möglich ist, erstellen Sie ein Mercurial Repository auf dem externen Rafisa Root Server. Die Commits sollen im Umfang möglichst klein sein und themenverwandt. Die Commits müssen in guter Qualität kommentiert werden. Jeden Abend soll ein Push auf den Server erfolgen. • Initialisierung des "Repository" auf dem Mercurial-Server. • Kleine Commits, z.B. beim Arbeitsjournal wurde einen Tageseintrag fertiggestellt. • Themenverwandte Commits, z.B. Business-Klassen wurden erweitert. • Täglicher Push auf den Server. \\ \\ Dokumentation: Um den Aufbau für den Auftraggeber möglichst transparent zu machen, erstellen Sie die notwendigen Use-Cases, ERM-Diagramme, aus welche folgende Angaben ableitbar sind: • Systemaufbau • Schnittstellen • Bestellungsabwicklung durch den Benutzer • Warenkorb-Funktionalität/System • Komplettes ERM-Diagramm der Persistenzklassen \\ \\ Technologien: Folgende Technologien werden vom Auftraggeber vorgegeben: • PHP OOP • JavaScript • jQuery • HTML5 • CSS • MySQL \\ \\ \\ \\ Warenkorb: Der Internet-Benutzer soll Artikel auswählen können und diese in den Warenkorb legen. Der Warenkorb soll durch den Benutzer editierbar sein. Der Warenkorb soll von der Artikelliste auswählbar sein und folgende Funktionalität enthalten: • Anzahl der bestellten Artikel änderbar, per Artikel. • Einzelne Artikel löschbar. • Der Warenkorb nach 15 Minuten Nichtbenutzung ungültig werden. • Anzeige der folgenden Felder: Beschreibung, Anzahl, Preis, Subtotal. • Die letzte Zeile der Artikel-Positionen/Zeilen soll das Gesamttotal der Ausgewählten Artikel ausgeben. • Die Subtotal- und Total-Werte werden bei jeder Aktion (Entfernen/Anzahl ändern) neu berechnet. • Korrekte Validierung der Daten. \\ \\ Zur Veranschaulichung, (die geschweiften Klammern bedeuten dass der Wert in einem Input-Feld dargestellt werden und die eckigen Klammern bedeuten einen Knopf): \\ \\ Beschreibung Anzahl Preis Subtotal 'Gloria' T-Schirt {2} 50.00 100.00 [entfernen] 'Gloria' Sonnenbrille {1} 95.00 95.00 [entfernen] \\ \\ Total 195.00 \\ \\ Artikel: Die Artikel werden als Vorarbeit als Testdaten in einer Datenbank und Artikel-Tabelle erfasst. Die Testdaten sollen in die neue Datenbank in dessen Artikel-Tabelle importiert werden. Dabei ist darauf zu achten, dass die korrekte Kollation gesetzt wurde, UTF-8. • Import in die neue Datenbank der als Vorarbeit erfassten Artikel-Testdaten. • UTF-8 als Kollation. • Beachten Sie dabei, dass die Felddefinitionen (VARCHAR, INT, TEXT, etc...) beider Tabellen und Datenbanken (Vorarbeit und Produktive) übereinstimmen. • Artikel bestehen aus folgenden Felder: Code/ID (VARCHAR 255), Titel (VARCHAR 255), Beschreibung (LONGTEXT), Preis (INT 11), Status (VARCHAR 255), Bild (BLOB). • Der Status eines Artikels kann folgende Zustände haben: Lieferbar, Nicht an Lager, Inaktiv. • Inaktive Artikel werden nicht angezeigt. \\ \\ Artikelliste: Die Anzeige der Artikel soll maximal 10 Artikel gleichzeitig anzeigen. Sollten mehr als 10 Artikel vorhanden sein, soll dem Benutzer eine Navigation angeboten werden, mit der der Benutzer weitere Artikel ansehen kann. Die Artikelanzeige in der Listenanzeige soll aus einem Titel, ein Foto, die Beschreibung auf maximal 200 Zeichen beschränkt und einen Preis. Bei jedem Artikel in der Liste soll es einen „in den Warenkorb“-Knopf geben und einen Knopf mit der man die Einzelansicht anzeigen kann. • Maximal 10 Artikel auf der Liste. • Seitenweise Navigation der Liste, wenn mehr als 10 Artikel vorhanden. • Listenansicht der Artikel mit Bild, Kurzbeschreibung (maximal 200 Zeichen) und Preis. • "In den Warenkorb" Knopf bei jedem Artikel der auf der Liste erscheint. \\ \\ Artikel-Detailansicht: Ein Artikel soll in einer Einzelansicht detaillierter beschrieben werden. Diese soll erscheinen wenn ein Benutzer einen Knopf in der Artikelliste anklickt. Pro Artikel, in der Artikelliste, sollen einen Knopf um diesen im Warenkorb aufzunehmen und einer um die Detailansicht anzuzeigen, vorhanden sein. Die Artikel-Einzelansicht bietet ein Bild, einen Titel, eine Beschreibung, einen Preis und einen „in den Warenkorb“-Knopf. Die Einzelansicht soll als Fenster angezeigt werden. • Artikel-Detailansicht mit Bild, Titel, Beschreibung und Preis. • "In den Warenkorb" Knopf. \\ \\ Bestellung: Die Artikelliste soll eine Möglichkeit bieten eine Bestellung Abzusenden. Zuerst wird der Warenkorb angezeigt. Sobald der Benutzer diesen Bestätigt muss ein Formular erscheinen wo der Benutzer seine Personalien eingeben kann. Die ersten zwei Punkte sind zwingend und müssen so kennzeichnet werden, dass ein Benutzer diese als Pflichtfelder wahrnimmt: • Formular mit Anrede, Name, Vorname, E-Mail-Adresse. • Formular mit Rechnungsadresse. • Formular mit Lieferadresse. • Formular mit Telefonnummer. • Korrekte Validierung der Daten. \\ \\ Wurde eine Bestellung erfolgreich abgeschlossen ist diese in der Datenbank zu speichern. Folgende Daten sind zu speichern: Bestellungsnummer, Bestellungsdatum, Name, E-Mail-Adresse, Telefonnummer (wenn vorhanden) und Adresse der Person die bestellt, Lieferadresse (wenn vorhanden) und die Artikel (in einem Textfeld) die die Person bestellt hat. Die Datumsfelder sind mit Linux-Timestamp Werte zu füllen. • Bestellungen bestehen aus folgenden Felder: Bestellungsnummer (VARCHAR 255), Bestellungsdatum (INT 11), Name (VARCHAR 255), Email (VARCHAR 255), Telefonnummer (VARCHAR 255), Adresse (TEXT), Lieferadresse (TEXT), Artikel (LONGTEXT). • Die Adressfelder sind folgend zu formatieren: die erste Zeile besteht aus einem Vornamen und Namen, die Zweite Zeile aus der Strasse und Hausnummer, die dritte Zeile aus der Postleitzahl und die Ortschaft, die letzte Zeile aus dem Land. Die Zeilen sind durch die '\r\n' Escape-Sequenz zu trennen. • Die Artikelliste im Artikel-Feld ist folgend zu formatieren: Artikelnummer, Beschreibung, Anzahl und Preis. Die einzelne Werte werden durch ein Semikolon ';' separiert. Jeder Artikel-Eintrag wird durch die '\r\n' Escape-Sequenz getrennt. • Bestellung wird gespeichert mit all den erforderlichen Daten. \\ \\ Zur Veranschaulichung des Aufbaus der Daten der Adressfelder: Max Muster\r\n Musterstrasse 1\r\n 8000 Zürich\r\n Schweiz\r\n \\ \\ Zur Veranschaulichung des Aufbaus der Daten des Artikelfeldes, die Werte sind nur als Beispiel zu betrachten: 01;'Gloria' T-Schirt;2;50.00\r\n 02;'Gloria' Sonnenbrille;1;95.00'\r\n' \\ \\ Fehlerbehandlung: Mögliche Fehler müssen abgefangen werden, z.B. wenn der MySQL Server nicht mehr funktioniert, soll nicht eine PHP interne Fehlermeldung erscheinen welche Rückschlüsse auf die Daten- und Projekt-Struktur zulässt. Die Fehler müssen durch eigene Meldungen/Fehlercodes ersetzt werden, z.B. 'Datenbankfehler D001' bei einem Fehlversuch sich beim MySQL-Server anzumelden, oder 'Datenbankfehler D002' bei einem misslungenem 'INSERT' Versuch. Diese Fehlercodes müssen dokumentiert werden. • Kritische Datenbank-Fehler werden abfangen. • Eigene Fehlercodes erzeugen. • Anzeige des Fehlercodes/Meldungen bei abgefangenen Fehler. \\ \\ Validation: Um eine korrekte Bestellung aufnehmen zu können müssen die Benutzereingaben überprüft werden. Pflichtfelder müssen überprüft werden. Bei fehlerhaften Eingaben muss der Benutzer darauf aufmerksam gemacht werden. • Beim Warenkorb muss überprüft werden ob die Anzahl eine Ganzzahl ist. • Bei der Angabe der Personalien des Benutzers sind die Pflichtfelder auf sinnvolle Eingaben zu überprüfen. • Der Benutzer soll auf nicht ausgefüllte Pflichtfelder aufmerksam gemacht werden. • Der Benutzer soll auf nicht korrekt ausgefüllte Felder aufmerksam gemacht werden. \\ \\ Diverses: • Die Benutzungsmasken sollen selbsterklärend, einfach und klar strukturiert sein. • Die Sicherheit soll durch den Einsatz der PHP-PDO-Bibliothek erhöht werden. • Es dürfen keine Daten physikalisch gelöscht werden, sondern nur als ‚gelöscht‘ markiert werden. • Jede Aktion soll in einer Logdatei festgehalten werden. Aus dieser Datei soll ersichtlich sein was, wann getätigt wurde. • Die Software muss objekt-orientiert und modularisiert programmiert werden. \\ \\ \\ \\ Tests: Die Aufgabe umfasst die Installation eines Webauftrittes der funktionell ist, jedoch ohne definitive Designelemente. Das Ganze soll ausgiebig getestet werden. • Dazu ist ein Testkonzept zu erstellen. • Die Tests sind gemäss Konzept durchführen. \\ \\ Mindestens folgende Testszenarien müssen durchgeführt werden: • Webshop anzeigen. • Warenkorb Artikelanzahl ändern, Artikelposition einfügen und entfernen. \\ \\ \\ \\ Code-Konventionen: • Namenskonvention: analog Java für selbstdefinierte Programmteile. Klassen sind ‚upper camel case‘, Methoden sind ‚lower camel case‘, Variablen sind ‚lower camel case‘ und Konstanten müssen ‚upper case underscore separated‘ sein. • Formatierung des Codes: Netbeans PHP Standard Formatierer. • Quellcode Dokumentation, Klassen Definition, ausführliche Kommentare inklusive Instanzierungs-Beispiele. Methoden ausführlich inklusive Parameter, Rückgabewerte und etwaige Throws. Konstanten und deren Bedeutung werden erklärt. • Variablen/Felder durch das ‚protected‘ Schlüsselwort geschützt, Zugriff auf diese nur durch Methoden. • Stringliterale wo möglich und sinnvoll als Konstanten. \\ \\ ====== 2Projektaufbauorganisation ====== **Tabelle 1: Projektaufbauorganisation** | Person | Rolle | Aufgaben | Verantwortung | | Herr Lade Basil | Kandidat | IPA durchführen und abschliessen | Die IPA vollständig und korrekt abschliessen | | Herr Windmeisser Jorge | Fachvor-gesetzter | Beurteilt die fachliche Richtigkeit der Arbeit | Steht bei Fachfragen zur Verfügung und bewertet die Fachkompetenzen des Kandidaten | | Herr Peter Tobias | Experte | Stellt die Qualität der fachlichen Beurteilung sicher Studiert den Bericht und leitet das Fachgespräch | Legt die Termine (Besuch kurz nach Start der Arbeit, zweiter Besuch gegen Ende der Arbeit sowie Präsentation und Fachgespräch) fest Entscheidet bei auftretenden Problemen über Massnahmen und meldet diese unverzüglich dem Chefexperten Liefert die Dokumente termingerecht an den Chefexperten | | Herr Achermann Martin | Zweit-Experte | Schreibt ausführliche und lesbare Notizen während der Präsentation, dem Fachgespräch und der Bewertung auf \\ | Unterstützt den Experten beim Bewerten der Arbeit. | \\ \\ \\ \\ ====== 3Vorkenntnisse ====== • OO Programmieren. • HTML, PHP, CSS, SQL, JavaScript/jQuery • Projektabwicklung. \\ \\ ====== 4Vorarbeiten ====== • Installation von MAMP/XAMPP-Software. • Installation und Konfiguration der Netbeans-IDE. • Erfassung von Testdaten (Artikel). \\ \\ ====== 5Firmenstandards ====== Code-Konventionen: • Namenskonvention: analog Java für selbstdefinierte Programmteile. Klassen sind ‚upper camel case‘, Methoden sind ‚lower camel case‘, Variablen sind ‚lower camel case‘ und Konstanten müssen ‚upper case underscore separated‘ sein. • Formatierung des Codes: Netbeans PHP Standard Formatierer. • Quellcode Dokumentation, Klassen Definition, ausführliche Kommentare inklusive Instanzierungs-Beispiele. Methoden ausführlich inklusive Parameter, Rückgabewerte und etwaige Throws. Konstanten und deren Bedeutung werden erklärt. • Variablen/Felder durch das ‚protected‘ Schlüsselwort geschützt, Zugriff auf diese nur durch Methoden. • Stringliterale wo möglich und sinnvoll als Konstanten. \\ \\ ====== 6Zeitplan ====== {{%5B2017-04-13%5D-ipa-bericht_html_4afea0081c15e028.gif?1107x869}}\\ \\ \\ ====== 7Arbeitsprotokolle ====== ===== 7.1Arbeitsprotokoll vom 13.04.2017 ===== ==== 7.1.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote1sym|1]]** | **Std. Verg.** ******[[#sdfootnote2sym|2]]** | **Std. Verbl.** ******[[#sdfootnote3sym|3]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 00:00h | 00:00h | 80:00h | - | Start der IPA | | 04:00h | 04:00h | 76:00h | Erstellung des Zeitplans | Unterstützung von Fachvorgesetzten und Berufsbildner in Form von Beratung Zeitplanvorlage von vergangenem Projekt und für die IPA entsprechend modifiziert | | 02:00h | 06:00h | 74:00h | Verfassung von Teil1 des IPA Berichtes | Vorlage des Berichts von vergangenem Projekt und für die IPA entsprechend modifiziert | | 00:25h | 06:25h | 73:35h | Expertenbesuch 1 (inklusive Aufgabenstellung analysieren und wesentliche Infos) | Beteiligte: Kandidat, Fachvorgesetzter und Experte | | 00:53 | 07:18h | 72:42h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 00:29h ||| \\ | \\ | | 07:18h | 07:18h | 72:42h | \\ | \\ | \\ \\ ==== 7.1.2Reflektion ==== Der erste Tag der IPA verlief bis auf weiteres reibungslos. Alle vorgenommenen Tätigkeiten konnten erfüllt werden, jedoch nahm der Zeitplan mehr Zeit in Beanspruchung als geplant. Die Wartezeit wurde durch die Überarbeitung der Darstellung (z.B. der Legende) überbrückt. Die verlorenere Zeit konnte jedoch wieder ausgeglichen werden, da die Vorlage des IPA Berichts eine Menge Zeit einsparte. Ausserdem war heute der erste Expertenbesuch, welcher positiv verlief. Als nächstes steht unteranderem die Erstellung der notwendigen Diagramme an. \\ \\ ===== 7.2Arbeitsprotokoll vom 18.04.2017 ===== ==== 7.2.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote4sym|4]]** | **Std. Verg.** ******[[#sdfootnote5sym|5]]** | **Std. Verbl.** ******[[#sdfootnote6sym|6]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 05:00h | 12:18h | 67:42h | Erstellung des Use Case Diagramms | User stories schreiben Use Case mit draw.io erstellt Endprodukt mit Fachvorgesetzten angeschaut | | 00:50h | 13:08h | 66:52h | Erstellung des Flowcharts | Flowchart mit draw.io erstellt | | 00:14h | 13:22h | 66:38 | Erstellung des ERM Diagramms | ERM Diagramm mit MySQL Workbench erstellt | | 00:50h | 14:12h | 65:48h | Erstellung des Klassendiagramms (Fortsetzung morgen) | Klassendiagramm mit draw.io begonnen | | 00:43 | 14:55h | 65:05h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 00:43h ||| \\ | \\ | | 07:37h | 14:55h | 65:05h | \\ | \\ | \\ \\ ==== 7.2.2Reflektion ==== Nach einer Osterpause ging es mit dem zweiten Tag der IPA weiter. Alle heutigen Tätigkeiten konnten erledigt werden, wobei das Use Case Diagramm viel mehr Zeit beansprucht hat als geplant. Der Grund dafür ist, dass ich bei der Erstellung des Diagramms zu weit gedacht habe und somit die Darstellung zu detailliert wurde. Ausserdem habe ich zusätzlich user stories („Anwendererzählung“) verfasst, damit ich eine bessere Vorstellung davon habe welche Use Cases schlussendlich ins Diagramm kommen. Zu guter Letzt besprach ich das Use Case Diagramm mit meinem Fachvorgesetzten. Die weiteren Diagramme konnte ich ohne grössere Probleme erstellen. Die durch das Use Case Diagramm verlorene Zeit konnte ich gut aufholen. Aus dem heutigen Tag ziehe ich die Lehre Sachen nicht unnötig zu komplizieren. Für den morgigen Tag muss ich gemäss Plan das Klassendiagramm fertigstellen, das Testkonzept verfassen, Libraries vergleichen und auswerten (jquery) und zu guter Letzt das Mercurial Repository initialisieren. \\ \\ ===== 7.3Arbeitsprotokoll vom 19.04.2017 ===== ==== 7.3.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote7sym|7]]** | **Std. Verg.** ******[[#sdfootnote8sym|8]]** | **Std. Verbl.** ******[[#sdfootnote9sym|9]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 03:44h | 17:39h | 61:21h | Fertigstellung des Klassendiagramms | Besprechung mit Fachvorgesetzten | | 03:19h | 20:58h | 58:02h | Erstellung des Testkonzepts | Besprechung mit Fachvorgesetzten | | 00:05h | 21:03h | 57:53h | Mercurial Repository initialisieren | Initialisierung durch Fachvorgesetzten | | 00:27h | 21:30h | 57:26h | Grundlegende Ordnerstruktur + erster commit | - | | 00:25h | 21:55h | 57:01h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:32h ||| \\ | \\ | | 07:20h | 21:55h | 57:01h | \\ | \\ | \\ \\ ==== 7.3.2Reflektion ==== Nach dem dritten Tag kann ich sagen, dass ich mir immer noch zu viele Sorgen und Gedanken mache. Ich komme zwar stetig voran, jedoch brauche ich manchmal ein bisschen mehr Zeit als eingeplant, was mich heute das erste Mal ein wenig heruntergezogen hat. Im Zeitplan stehe ich momentan ein wenig im Hintertreffen. Das heisst, dass ich heute die Libraries nicht evaluieren konnte. Stattdessen habe ich die Initialisierung des Mercurial Repository, durch den Fachvorgesetzten, die grundlegende Ordnerstruktur und den ersten Commit vorgezogen, da diese Tätigkeiten im Vergleich zur Library Evaluation weniger Zeitaufwand benötigt. Jedoch hatte mein Arbeitsrechner in den letzten Minuten ein Performanceproblem, weshalb ich ihn neustarten musste. Das kostete mir unnötig Zeit. Für morgen nehme ich mir vor mit einem kühleren Kopf an die Sache ran zu gehen. Des Weiteren muss ich morgen die Library Evaluation nachholen und mit der Programmierung beginnen. \\ \\ ===== 7.4Arbeitsprotokoll vom 20.04.2017 ===== ==== 7.4.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote10sym|10]]** | **Std. Verg.** ******[[#sdfootnote11sym|11]]** | **Std. Verbl.** ******[[#sdfootnote12sym|12]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 01:45h | 22:40h | 56:20h | Libraries evaluieren | - | | 00:55h | 23:40h | 55:25h | Libraries auswerten entscheiden | - | | 01:45h | 24:30h | 54:40h | Datenbank aufbereiten | Testdaten als Vorarbeit | | 02:20h | 26:50h | 52:20h | Persistence Layer (Basisklassen) implementieren | Klasse aus einem vorherigen Projekt übernommen und modifiziert bzw. angepasst | | 00:22h | 27:12h | 51:58h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:09h ||| \\ | \\ | | 06:27h | 27:12h | 51:58h | \\ | \\ | \\ \\ ==== 7.4.2Reflektion ==== Am heutigen Tag konnte ich alle Tätigkeiten erfüllen und die unerledigten von gestern nachholen. Im Gegensatz zu gestern konnte ich mich viel besser konzentrieren und bin weniger erschöpft als am Vortag. Die Verbesserte Konzentration resultiert daraus, dass ich ganz viele fünf Minuten Pausen gemacht habe. Im Chrome Browser habe ich eine Erweiterung (Pomodoro timer) installiert, die mir alle 25 Minuten sagt, dass ich fünf Minuten Pause machen soll. Für Morgen stehen die Implementierung der Basisklassen des Business und Presentation Layers an. \\ \\ ===== 7.5Arbeitsprotokoll vom 21.04.2017 ===== ==== 7.5.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote13sym|13]]** | **Std. Verg.** ******[[#sdfootnote14sym|14]]** | **Std. Verbl.** ******[[#sdfootnote15sym|15]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 03:25h | 30:37h | 48:21h | Business Layer (Basisklassen) implementieren | Klasse aus einem vorherigen Projekt übernommen und modifiziert bzw. angepasst | | 03:10h | 33:57h | 45:15h | Presentation Layer (Basisklassen) implementieren | Klasse aus einem vorherigen Projekt übernommen und modifiziert bzw. angepasst | | 00:20h | 34:17h | 44:55h | Zweites Expertengespräch | Vorhandene Personen: Experte, Fachvorgesetzter, Kandidat | | 00:30h | 34:47h | 44:25h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:30h ||| \\ | \\ | | 07:25h | 34:47h | 34:47h | \\ | \\ | \\ \\ ==== 7.5.2Reflektion ==== Heute habe ich die Hälfte der IPA erreicht und bin einigermassen erleichtert. Alle gesetzten Tätigkeiten konnten erreicht werden, obwohl zwischendurch noch das zweite Expertengespräch stattfand, welches positiv verlaufen ist. Heute konnte ich mich weitgehend gut konzentrieren. Zwischendurch gab es ein Stimmungstief, aus dem ich schnell wieder raus kam. Nächste Woche werde ich die Action Basisklassen implementieren und mit der eigentlichen Programmierung des Webshops anfangen. \\ \\ ===== 7.6Arbeitsprotokoll vom 25.04.2017 ===== ==== 7.6.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote16sym|16]]** | **Std. Verg.** ******[[#sdfootnote17sym|17]]** | **Std. Verbl.** ******[[#sdfootnote18sym|18]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 06:20h | 41:07h | 28:27h | Action Layer (Basisklassen) implementieren | Klasse aus einem vorherigen Projekt übernommen und modifiziert bzw. angepasst | | 00:20h | 41:27h | 28:05h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:37h ||| \\ | \\ | | 06:40h | 41:27h | 28:05h | \\ | \\ | \\ \\ ==== 7.6.2Reflektion ==== Heute konnte ich leider nicht alle Tagesziele erreichen. Die Produktliste wollte ich heute schon implementieren, jedoch hat die Action Layer Implementation länger gedauert als ich gedacht hatte. Zum einen liegt es daran, dass ich sorgfältige Kommentare schreibe und zum anderen habe ich diverse Methoden angepasst bzw. in kleinere Methoden aufgeteilt. Das klingt nicht nach viel Arbeit, aber damit es einfach aussieht muss man viel Zeit investieren. Zwischendurch, am späten Nachmittag, hatte ich eine Schreibblockade. Deshalb musste ich eine längere Pause einlegen. Danach war mein Kopf wieder frei genug und ich konnte mit der Arbeit fortfahren. Für Morgen muss ich den Bericht um die Beschreibung der Aktion Layer Basisklassen erweitern und natürlich die Implementation der Artikelliste nachholen. \\ \\ ===== 7.7Arbeitsprotokoll vom 26.04.2017 ===== ==== 7.7.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote19sym|19]]** | **Std. Verg.** ******[[#sdfootnote20sym|20]]** | **Std. Verbl.** ******[[#sdfootnote21sym|21]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 01:42h | 41:27h | 28:05h | Action Layer (Basisklassen) dokumentieren | - | | 04:10h | 45:37h | 23:55h | Artikelliste implementiert | - | | 00:50h | 46:13 | 23:05h | Produktdetailansicht implementiert | - | | 00:28h | 46:41h | 22:37h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:30h ||| \\ | \\ | | 07:10h | 46:41h | 22:37h | \\ | \\ | \\ \\ ==== 7.7.2Reflektion ==== Heute habe ich die Dokumentation des Action Layer und die Implementation der Artikelliste nachgeholt. Weiterhin gelang es mir die Produktdetailansicht zu implementieren, jedoch reichte die Zeit nicht mehr für den Warenkorb. Im Verlauf des Tages habe ich beim Programmieren hin und wieder Probleme gehabt. Diese konnte ich jedoch relativ schnell lösen. Meistens waren diese Probleme auf Flüchtigkeitsfehler zurückzuführen. Ausserdem hat mich die Firewall aussergewöhnlich oft rausgeworfen. Damit ich Zugang zum Internet habe, muss ich bei der Firewall angemeldet sein. Das war ziemlich ärgerlich. Jedoch hat mir das sehr wenig Zeit gekostet. Morgen muss ich die Implementierung des Warenkorbs nachholen und die Persistierung der Bestellung implementieren. Ich muss mich beeilen, denn die IPA ist schon fast vorüber. \\ \\ ===== 7.8Arbeitsprotokoll vom 27.04.2017 ===== ==== 7.8.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote22sym|22]]** | **Std. Verg.** ******[[#sdfootnote23sym|23]]** | **Std. Verbl.** ******[[#sdfootnote24sym|24]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 06:52h | 53:33h | 15:45h | Warenkorb implementieren | - | | 00:17h | 53:50h | 15:28h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 01:13h ||| \\ | \\ | | 07:09h | 53:50h | 15:28h | \\ | \\ | \\ \\ ==== 7.8.2Reflektion ==== Heute ist der dritt-letzte Tag der IPA. Für den heutigen Tag habe ich mir vorgenommen, fertig zu werden mit dem Programmieren. Doch das muss ich auf morgen verschieben. Im Grossen und Ganzen ging es mir heute wieder besser, doch gegen Abend verliere ich an Konzentration und kämpfte mich durch. Für morgen muss ich die Persistierung der Bestellung implementieren. \\ \\ ===== 7.9Arbeitsprotokoll vom 28.04.2017 ===== ==== 7.9.1Zeitübersicht: ==== | **Std. Heute[[#sdfootnote25sym|25]]** | **Std. Verg.** ******[[#sdfootnote26sym|26]]** | **Std. Verbl.** ******[[#sdfootnote27sym|27]]** | **Ausgeführte Arbeit** | **Bemerkungen** | | 06:15h | 60:05h | 09:03h | Persistierung einer Bestellung implementiert | - | | 00:10h | 60:15h | 08:53h | Arbeitsprotokoll Erfassung | Zeitdauerberechnung der einzelnen Tätigkeiten Reflektion aufschreiben | | TOTAL\\ Überstunden: 00:05h ||| \\ | \\ | | 06:25h | 53:50h | 15:28h | \\ | \\ | \\ \\ ==== 7.9.2Reflektion ==== Heute konnte ich die Persistierung einer Bestellung implementieren. Momentan wird nur überprüft, ob alle Pflichtfelder ausgefüllt sind. Ein weiterer Schritt ist es die Benutzereingaben auf Falschheit zu überprüfen und ungewollte Character zu entfernen bzw. unschädlich zu machen. Heute hatte ich sehr viel weniger Stress als gestern. Die nächsten Schritte sind, Benutzereingaben validieren und „säubern“ und die Loggingfunktionalität implementieren. \\ \\ ====== Teil 2 ====== ====== 8IPA Kurzfassung ====== Der IPA Bericht besteht aus mehreren Teilen: * Teil 1 * Teil2 * Anhang Im **Teil 1** befinden sich grösstenteils die Informationen, welche auch auf PkOrg auffindbar sind. Sprich beteiligte Personen, Aufgabenstellung, an welchen Tagen an der IPA gearbeitet wird usw. Des Weiteren sind auch die Tagesprotokolle und der Zeitplan im Teil 1 untergebracht. **Teil 2** handelt sich dann um die Realisierung des Projekts bzw. deren Beschreibung. Die Struktur der Kapitel und Unterkapitel mitsamt deren Titelbezeichnungen versucht sich so gut wie möglich an den Tätigkeiten des Zeitplans zu orientieren. **Im Anhang** wird der selbstgeschriebene Quellcode aufgelistet und bekommt ein eigenes Inhaltsverzeichnis. Die Struktur des Inhaltsverzeichniss orientiert sich an die Ordnerstruktur des Projekts. **Aufgabenstellung:** Laut Aufgabenstellung, soll ein Webshop für einen Internetauftritt, welcher nach der IPA realisiert wird, programmiert werden. Der Nutzer sieht, nachdem er den webshop aufgerufen hat, als erstes die Artikelliste. Von dort aus kann er über einen Artikel mehr Informationen erfahren oder zum Warenkorb hinzufügen. Ausserdem kann die Artikelliste geblättert werden, um sich weitere Artikel anschauen zu können. Der Nutzer kann von der Artikelliste aus auf den Warenkorb zugreifen und deren Positionen bearbeiten (Quantität ändern) oder entfernen. Weiterhin kann der Nutzer seine Bestellung speichern, nachdem dieser ein Bestellformular ausfüllt. \\ \\ ====== 9Planen ====== ===== 9.1User Stories ===== Normalerweise sind user stories (deutsch „Anwendererzählung“) im Bereich der agilen Softwareentwicklung in Verwendung und dienen unteranderem dazu einzelne zusammengehörende Arbeitsschritte in einer Arbeitseinheit zusammenzufassen, welche dann in einem Sprint erledigt werden können soll. Bei dieser IPA wird allerdings nicht die agile Softwareentwicklung (z.B. scrum) eingesetzt, sondern IPERKA. Die user stories werden hier lediglich dazu verwendet, um eine bessere Vorstellung davon zu bekommen, welche use cases es abzudecken gilt. Eine user story wird in der Regel in einem Satz formuliert und besitzt keinen festen Standard bezüglich der Formulierung. Es gibt jedoch eine Vorlage welche häufig genutzt wird:\\ „Als möchte ich , um “. Dementsprechend gibt es auch eine kürzere Variante, sprich: „Als möchte ich . * Als Webshopbesucher möchte ich eine Artikelliste einsehen können, um einen Überblick des Artikelangebotes zu bekommen. * Als Webshopbesucher möchte ich die Artikelliste blättern können, um weniger scrollen zu müssen. * Als Webshopbesucher möchte ich eine Artikeldetailansicht einsehen können, um weitere Details über den Artikel zu erfahren. * Als Webshopbesucher möchte ich ein Artikel dem Warenkorb hinzufügen, um später diesen oder mehrere Artikel kaufen bzw. bestellen zu können. * Als Webshopbesucher möchte ich die Artikelanzahl in einer Warenkorbposition erhöhen / senken, um den gleichen Artikel in einer höheren Quantität bestellen zu können. * Als Webshopbesucher möchte ich eine Warenkorbposition entfernen können. * Als Webshopbesucher möchte ich eine Bestellung aufgeben (im Bereich der IPA nur speichern) können. \\ \\ ===== 9.2Diagramme ===== ==== 9.2.1Verwendetes Tool ==== Für die Erstellung der Diagramme wurde die kostenlose Webapplikation **draw.io** verwendet. Draw.io bietet diverse Speicheroptionen an. Die Diagramme können entweder lokal auf der Festplatte oder in der Cloud gespeichert werden. Dabei bietet draw.io folgende Cloud-Dienste an: Google Drive, Dropbox, OneDrive. Zusätzlich werden umfangreiche Exportoptionen angeboten. Wie bei Excel kann eine Datei mehrere Blätter beinhalten. Weiterhin können mehrere Mitarbeiter das gleiche Dokument gleichzeitig bearbeiten. ==== 9.2.2Use-Cases ==== {{%5B2017-04-13%5D-ipa-bericht_html_c8f610b2d211ed2d.png?547x504}}**Abbildung 1: Use-Cases** \\ Das Use-Case Diagramm visualisiert die verschiedenen Aktionen, die ein Nutzer im Webshop ausführen kann. ==== 9.2.3Flussdiagramm ==== {{%5B2017-04-13%5D-ipa-bericht_html_135a66bb49e56e62.png?329x763}}**Abbildung 2: Flussdiagramm\\ **\\ \\ Das Flussdiagramm visualisiert den kompletten webshop Ablauf, der vom Anschauen der Artikelansicht bis hin zum Persistieren der Bestellung reicht. \\ \\ ==== 9.2.4ERM-Diagramm ==== {{%5B2017-04-13%5D-ipa-bericht_html_961dbb731dbd42be.png?640x416}}**Abbildung 3: ERM Diagramm\\ **\\ \\ Die Beststellungstabelle hat keine Referenz zur Artikeltabelle, da die ID des Artikels im Adresse-Tabellenfeld der Bestellungstabelle gespeichert ist. Ausserdem ist der Datentyp der ID ein varchar und nicht ein int, da die ID sich durch den Tabellenname und einem GUID V4 String zusammensetzt. Beispiel: Artikel_cf741e89-321b-4564-abc8-a82e6d0a77a6 oder Bestellung_33519ebf-e98f-4cc7-88d5-9bb41e118990 \\ \\ ==== 9.2.5Klassendiagramme ==== {{%5B2017-04-13%5D-ipa-bericht_html_da54716e1091a57c.png?610x746}}**Abbildung 4: Klassendiagramm\\ **\\ \\ Das Klassendiagramm visualisiert die Beziehung der einzelnen Aktionen zur Basisklasse und deren Interface. Die **SaveOrderAction** ist die einzige Aktion, welche Daten in die Datenbank persistiert da der Warenkorb sich in einer temporären Session befindet und nicht in die Datenbank persistiert wird. \\ \\ ===== 9.3Testkonzept ===== Ziel eines Testkonzepts ist, einen Überblick davon zu bekommen was auf welche Art und Weise getestet wird. ==== 9.3.1Modultest / Unittest ==== Hierbei werden kleinst-mögliche, voneinander isolierte und testbare Funktionalitäten getestet. Bezüglich dieser IPA beschränken sich die Modultests auf die Aktionen bzw. Actions wie zum Beispiel „UpdateCartPosition“ oder „SaveOrderAction“. Bei der „SaveOrderAction“ kann zum Beispiel getestet werden, ob die Benutzereingaben des Kontaktformulars valide sind oder ob die Bestellung (Order) erfolgreich gespeichert wurde. **Tabelle 2: Modultestplan** | Test Nr. | Testbeschreibung | Erwartetes Resultat | Effektives Resultat | Bestanden | | | \\ | \\ | \\ | \\ | | | \\ | \\ | \\ | \\ |\\ \\ ==== 9.3.2Integrationstest ==== Hierbei wird die Funktionalität der Zusammenarbeit von unabhängigen Komponenten in einem System getestet. Der Integrationstest wird erst durchgeführt, sobald alle Modultests bzw. Unittests erfolgreich durchgelaufen sind. \\ \\ Der Integrationstest beschränkt sich in diesem Fall auf das Subsystem Webshop und ob dieser, nachdem der Nutzer ihn von der Homepage aus aufgerufen hat, erfolgreich angezeigt wird. **Tabelle 3: Integrationstestplan** | Test Nr. | Testbeschreibung | Erwartetes Resultat | Effektives Resultat | Bestanden | | | Der Webshop wird korrekt angezeigt, nachdem der Nutzer ihn aufgerufen hat. | Webshop korrekt angezeigt | \\ | \\ | \\ \\ ==== 9.3.3GUI Testing ==== Hierbei wird auf das korrekte Verhalten der Benutzeroberfläche bzw. GUI getestet. Das heisst, ist z.B. der Zurück- oder Vorwärtsknopf deaktiviert, wenn sich die Artikelliste auf der ersten bzw. letzen Seite befindet. Oder wird die Artikeldetailansicht mit den korrekten Artikeldaten angezeigt. \\ \\ **Tabelle 4: GUI Testplan** | Test Nr. | Testbeschreibung | Erwartetes Resultat | Effektives Resultat | Bestanden | | **Artikelansicht** ||||| | | Der Nutzer klickt in der Artikelliste auf den Vorwärtsknopf, damit die nächsten zehn Artikel angezeigt werden. | Die nächsten zehn Artikel werden angezeigt | \\ | \\ | | | Der Nutzer klickt in der Artikelliste auf den Zurückknopf, damit die vorherigen zehn Artikel angezeigt werden. | Die vorherigen zehn Artikel werden angezeigt | \\ | \\ | | | Der Nutzer klickt auf den „Details ansehen“ Knopf, welcher dafür sorgt, dass sich die Detailansicht öffnet. | Die Detailansicht wird geöffnet | \\ | \\ | | | Der Nutzer klickt auf den Schliessknopf, um die Detailansicht zu schliessen. | Die Detailansicht wird geschlossen | \\ | \\ | | | Der Nutzer klickt auf den „Zum Warenkorb hinzufügen“ Knopf, um den Artikel dem Warenkorb hinzuzufügen. | Der Artikel wird dem Warenkorb hinzugefügt | \\ | \\ | | | Der Nutzer klickt auf den „Warenkorb anzeigen“ Knopf, um den Warenkorb anzuzeigen. | Der Warenkorb wird angezeigt | \\ | \\ | | **Warenkorb** ||||| | | Der Nutzer erhöht bzw. senkt die Artikelanzahl, somit wird das Subtotal und Total neu berechnet. | Das Subtotal und Total wird neu berechnet | \\ | \\ | | | Der Nutzer klickt auf den „Entfernen“ Knopf, damit die Warenkorbposition entfernt wird. | Die Artikelposition wird entfernt | \\ | \\ | | | Der Nutzer klickt auf den „Bestellen“ Knopf, um das Kontaktformular anzuzeigen. | Das Kontaktformular wird angezeigt | \\ | \\ | | **Kontaktformular** ||||| | | Der Benutzer wird allenfalls auf Falscheingaben hingewiesen, nachdem er auf den „Bestellung speichern“ Knopf geklickt hat. | Der Nutzer wird auf Falscheingaben hingewiesen | \\ | \\ | | | Der Nutzer wird darauf hingewiesen, nachdem alle Eingaben korrekt getätigt und der „Bestellung speichern“ Knopf geklickt wurden, dass seine Bestellung erfolgreich gespeichert wurde. | Der Nutzer wird auf die erfolgreiche Speicherung der Bestellung hingewiesen | \\ | \\ | \\ \\ \\ \\ ====== 10Entscheiden ====== ===== 10.1Library Auswahl ===== Im Rahmen der IPA wird ausschliesslich eine Library verwendet namentlich jquery. Nun was gibt es da zu entscheiden? Von jquery gibt es drei verschiedene Versionen, welche unterschiedliche Browser unterstützen. Das heisst z.B. jquery in der Version 1 unterstützt den Internet Explorer von 6-8 und jquery Version zwei und drei nicht mehr. Nun besteht die Entscheidung daraus, ob es wichtig ist den Internet Explorer 6 zu unterstützten und allenfalls Sicherheitslücken in Kauf zu nehmen oder die altern Internet Explorer einfach links liegen zu lassen. ===== 10.2Evaluation der jquery Versionen ===== Um die Evaluation so effizient wie möglich zu gestalten, ist die Entscheidungsmatrix ein passendes Werkzeug. Die Entscheidungsmatrix visualisiert die zur Verfügung stehenden Optionen, die einzelnen Kriterien und deren Gewichtung und wie die Punkteverteilung in den jeweiligen Kriterien pro Produkt ausfällt. All diese Informationen werden in einer übersichtlichen Tabelle dargestellt. **Tabelle 5: jquery Evaluation** | **Optionen (Alternativen)** |||||||| | **Kriterien** | **Gewichtung** | **jquery 1** || **jquery 2** || **jquery 3** || | \\ | \\ | Bewertung | Total | Bewertung | Total | Bewertung | Total | | Browser Support (bis 2012) | 1 | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | | Browser Support (ab 2013) | 4 | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | | Sicherheit | 5 | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | 3 | %%**%% Fehlerhafter Ausdruck %%**%% | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | | Community Support | 5 | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | 3 | %%**%% Fehlerhafter Ausdruck %%**%% | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | | Verbreitung | 3 | 4 | %%**%% Fehlerhafter Ausdruck %%**%% | 3 | %%**%% Fehlerhafter Ausdruck %%**%% | 2 | %%**%% Fehlerhafter Ausdruck %%**%% | | **Total** | - | - | 0 | - | 0 | - | 0 | ===== 10.3 Auswertung und Entscheidung ===== ==== 10.3.1Auswertung ==== Beim Kriterium „Nutzung“ erzielt jquery 1 überraschenderweise die höchste Punktzahl und jquery 3 die kleinste. Dies geht aus einem Blog Post 1(Stand: 29.03.2017) von Snyk hervor. Snyk ist ein kostenpflichtiger Dienst und testet die Dependencies(deutsch Abhängigkeiten), wie z.B. jquery, in Webapplikationen auf Sicherheitslücken und behebt diese. Daraus ist zu schliessen, dass viele Webapplikationen bzw. Webseiten ihre jquery Version nicht auf die aktuellste Version upgraden, da dies, gerade bei umfangreicheren Projekte, ein grösseres Unterfangen darstellt. Hingegen ist das Ergebnis bezüglich des Kriteriums „Sicherheit“ weniger überraschend, da alte Internet Explorer Versionen Sicherheitslücken mit sich bringen. ==== 10.3.2Entscheidung ==== Im Rahmen der IPA würde der Support von sehr alten Browsern höchstwahrscheinlich den Rahmen sprengen und ist ergo auch kein Bestandteil der IPA. Die Entscheidung fällt auf jquery 3, obwohl jquery 1 bei der Nutzung am besten Abgeschnitten hat. Nur weil jquery 1 eine grosse Nutzerschaft geniesst, bedeutet das nicht, dass dies unterstützt werden soll, vor allem bezüglich der Sicherheit. Weiterhin zeigen die Benutzerstatistiken der weltweiten Browsernutzung auf, dass der Internetexplorer so gut wie gar nicht mehr genutzt wird, beziehungsweise nicht mehr der Marktführer ist. \\ \\ ====== 11Realisierung ====== ===== 11.1Repository initialisieren ===== Der Fachvorgesetze hat das Mercurial Repository initialisiert, da er dazu die nötigen Adminrechte besitzt. ==== 11.1.1Erster Commit ==== Der erste Commit beinhaltet die grundlegende Projektstruktur und allenfalls die **.hgignore** Datei, in der festgelegt werden kann, welche Dateien nicht versioniert werden sollen wie z.B. temporäre Microsoft Office Dateien. Netbeans graut die ignorierten Dateien in der Projektstrukturansicht aus. ===== 11.2Datenbank Aufbereitung ===== ==== 11.2.1Testdaten ==== Als Vorarbeit wurden für die Datenbank Testdaten erfasst, damit dies nicht während der IPA gemacht werden muss. Die Testdaten spiegeln die Artikel, welche schlussendlich nach der IPA erfasst werden, nicht wieder. Da neben dem Programmieren auch die Interesse in PC-Hardware besteht, handeln sich die Testdaten um diverse Hardware Komponenten. ==== 11.2.2Export der Testdaten ==== Teil der Vorgabe ist, dass die Artikelbilder in der Datenbank als BLOB (binary large object) persistiert werden, anstatt auf dem Server. Die binären Bilddaten werden in der hexadezimalen Notation im Tabellenfeld hinterlegt. Und hierbei trat das erste Problem auf. Die Datenbankverwaltung wird mit MySQL Workbench gemacht, was nicht das eigentliche Problem ist. Beim Exportieren einer Datenbank wird der Inhalt der BLOB Tabellenfelder standardmässig nicht in der hexadezimalen Notation sondern als normaler Text exportiert. ==== 11.2.3Import der Testdaten ==== Nach erfolgreichem Import des MySQL Dumps, stürzt MySQL Workbench beim Versuch den Inhalt des BLOB Tabellenfelds darzustellen ab. Das liegt daran, weil der Inhalt des BLOB Tabellenfelds normaler Text ist und keine binäre Zeichenfolge. Nach einer schnellen Recherche im Internet, lieferte Google keine zufriedenstellende Lösung für das spezifische Problem. Die Lösung besteht darin, die Exporteinstellungen in MySQL Workbench zu ändern. Jedoch ist diese eine Einstellung ein wenig versteckt, denn sie befindet sich in den erweiterten Einstellungen. Die Sache ist, dass die MySQL Workbench GUI nicht klar kommuniziert, dass es überhaupt erweiterte Exporteinstellungen gibt. In den erweiterten Einstellungen gibt es dann die Einstellungsmöglichkeit, binäre Tabellenfelder in der hexadezimalen Notation zu exportieren. Sobald diese Einstellungsoption ausgewählt wurde, merkt sich das MySQL Workbench. Nach erneutem Exportieren und Importieren war es nun möglich den BLOB Inhalt anzuzeigen, ohne dass dabei Workbench abstürzt. \\ \\ ===== 11.3Persistence Layer Basisklassen ===== Im Persistence Layer befinden sich Klassen, welche dafür zuständig sind Informationen an einem Ort zu persistieren, z.B. in eine Datenbank oder in eine Sessionvariable. ==== 11.3.1Database ==== Die Datenbankklasse ist dafür zuständig Daten aus der Datenbank herauszulesen und zu persistieren. Dafür bietet sie entsprechende Variablen und Methoden, welche im Klassendiagramm aufgeführt sind. === 11.3.1.1Fehlercodes === Die Datenbankklasse besitzt Fehlercodes welche dann im Browser nach einem aufgetretenen Fehler angezeigt werden. Dies wird gemacht, damit nach aussen nicht sichtbar ist wie das Programm aufgebaut ist. * Datenbankfehler D001: Fehler beim Aufbau der Datenbankverbindung * Datenbankfehler D002: Fehler beim Persistieren der Daten in die Datenbank * Datenbankfehler D003: Fehler beim Zählen der Tabellenreihen * Datenbankfehler D004: Fehler beim Laden eines Datensatzes * Datenbankfehler D005: Fehler beim Laden der Datensätze ==== 11.3.2SessionHelper ==== Der SessionHelper bietet Helper Methoden an, um auf bestehende Sessionvariablen zugreifen zu können oder neue zu erstellen. Ausserdem kann ein Sessiontimeout gestartet werden, der laut der Projektbeschreibung nach 15 Minuten abläuft. Diese Helper Methoden sind im Klassendiagramm aufgelistet. ==== 11.3.3Logging ==== Jede PHP Aktion geht durch den ActionHypervisor, deshalb wird dort der Aktivitätslog geschrieben. Ein Aktivitätslog beinhaltet Datum und Uhrzeit, die ausgeführte Aktion und zusätzliche Postparameter, falls vorhanden. Datenbankfehler werden direkt von der entsprechenden Methode aus in die Logdatei geschrieben. Diese Logeinträge beinhalten neben Datum und Uhrzeit, den Stacktrace und das ausgeführte sql Statment bzw. den Connectionstring, falls bei der Herstellung der Datenbankverbindung ein Fehler aufgetreten ist. \\ \\ ===== 11.4Business Layer Basisklassen ===== Der Business Layer ist für Berechnungen zuständig. Weiterhin beinhaltet der Business Layer Entities. Ein Entity ist eine Repräsentation einer Tabellenreihe in Form eines PHP Objekts. Die Tabellendaten werden zusammen mit den entsprechenden Feldnamen als assoziatives Array im Entity Objekt gespeichert. **Beispiel**: $this->data[Entity::ID] = „GUID v4 string“ ==== 11.4.1EntityInterface ==== Das Interface beinhaltet Methoden welche die Entity Klasse implementieren muss. In anderen Worten ausgedrückt: Das Interface ist ein Vertrag, bei dem alle Klassen, die ihn unterschreiben, Folge leisten müssen. Ausserdem erinnert die IDE (Integrierte Entwicklungsumgebung) den Programmierer daran, die Methoden, welche im Interface deklariert sind, in der jeweiligen Klasse einzufügen. Wissenswert ist auch, dass PHP mehrere Interface Implementierungen bei einer Klasse zulässt, im Gegensatz zur Vererbung. **Methodendefinitionen von EntityInterface:** * set() * setData() * get() * getData() ==== 11.4.2Entity Klasse ==== Die Entity Klasse implementiert die vordefinierten Methoden und fügt weitere hinzu. **Zusätzliche Methoden:** * getGUID() * getFields() * getClass() * %%__%%toString() \\ \\ ===== 11.5Presentation Layer Basisklassen ===== Der Presentation Layer sorgt für die Darstellung des Webshops. Jedes Element auf der Webseite wird durch ein Widget repräsentiert. Das Widget ist ein für sich isoliertes PHP Objekt das den entsprechenden HTML Code enthält. Widgets können jedoch miteinander kombiniert werden, so dass ein „grösseres“ Widget entsteht. Die JavaScript Library „React“ z.B. verfolgt ein vergleichbares Konzept, nur dass es dort keine Widgets sondern „Components“ sind. Ein Widget kann von einem einfachen HTML Inputfeld bis zu einem komplett funktionierenden Warenkorb reichen. ==== 11.5.1WidgetInterface ==== Das WidgetInterface definiert alle für die Widget Klasse wichtigen Methoden. **Methodendefinitionen von WidgetInterface:** * **prepare()** * **setLabel()** * **getLabel()** * **setValue()** * **getValue()** * **setActionName()** * **setTooltip()** * **addCssClass()** ==== 11.5.2Widget Klasse ==== Die Widget Klasse implementiert die Methoden aus dem Interface, jedoch mit einer Ausnahme. Die prepare() Methode wird erst im jeweiligen Widget selbst implementiert, da sich der HTML Code von Widget zu Widget unterscheidet. Ausserdem ist die Widget Klasse abstrakt, da sie nie direkt aufgerufen bzw. instanziiert wird. Ein spezifisches Widget kann je nach Bedarf zusätzliche Funktionalitäten implementieren. Jedoch muss sich diese Funktionalität eindeutig von anderen Widgets unterscheiden, dabei spricht man von generalisieren und spezialisieren. \\ \\ ===== 11.6Action Basisklassen ===== Eine Aktion bzw. Action setzt eine Kette von Arbeitsschritten in Bewegung. Eine Action kann zum Beispiel dafür sorgen, dass ein Artikel im Warenkorb hinterlegt wird. Dabei werden alle notwendigen Schritte ausgeführt. Wie bei einem Yin Yang hat eine Action zwei Hälften, eine JavaScript und eine PHP. - Yin Yang - JavaScript Ein JavaScript Event, namens **ClickListener**, wartet darauf dass irgendein HTML Element geklickt wird. Hat das geklickte Element ein **data-action** Attribut, dessen Wert der Action Name ist z.B. **addToCartAction**, wird eine externe JavaScript Datei geladen. Der Dateiname entspricht dem Wert des **data-action** Attributs. Auf diese Art ist es dem **ClickListener** möglich auf die **perform** Methode der jeweilig geladenen JavaScript Datei zuzugreifen und auszuführen. Dabei übergibt der „ClickListener“ das zuvor geklickte Element der **perform** Methode als Parameter. - Yin Yang - PHP Die **perform** Methode führt ein ajax Request aus und spricht index.php an. Dabei wird der Aktionsname und im Beispiel von **addToCartAction** die Artikel ID per POST Methode übertragen. Von index.php aus wird der **ActionHypervisor** aufgerufen. Der **ActionHypervisor** führt dann die entsprechende PHP Aktion aus. So gesehen ist **ActionHypervisor** das Äquivalent zum **ClickListener**, da beide darüber entscheiden welche Aktion ausgeführt werden soll. Wenn seitens PHP die Aktion erfolgreich ausgeführt wurde, gibt PHP per echo Statement eine entsprechende Meldung oder ein neues Widget, wenn die Aktion vorsieht ein bestehendes Widget zu aktualisieren, aus. Natürlich wird bei einem Fehler auch ein echo Statement gemacht. ==== 11.6.3Viele Wege führen zum JavaScript ==== Das vorhin erwähnte echo Statement ist essentiell, da dessen Inhalt an das JavaScript Gegenstück der Aktion weitergereicht wird. Schlussendlich wird diese Information im DOM (document object model) eingespeist. ==== 11.6.4Beteiligte Akteure ==== Der Übersicht halber werden nochmal alle „Akteure“ aufgelistet, die dafür zuständig sind eine Aktion auszuführen. * **ClickListener:** JavaScript * **Auszuführende Aktion:** JavaScript * **ActionHypervisor:** PHP * **Auszuführende Aktion:** PHP \\ \\ ===== 11.7Artikelliste ===== {{%5B2017-04-13%5D-ipa-bericht_html_df3aab5fde2d3b28.png?601x580}}**Abbildung 5: Artikelliste** Die Artikelliste wir angezeigt, nachdem auf der Homepage den webshop Knopf gedrückt wurde. Die Artikelliste zeigt nur zehn Artikel auf einmal an. Damit die nächsten zehn Artikel angezeigt werden können, befinden sich am Ender der Liste zwei Knöpfe, mit denen die Artikelliste vor und zurück geblättert werden kann. Pro Artikel in der Liste befinden sich zwei Knöpfe (siehe Abbildung oben). Der eine öffnet die Detailansicht und der andere fügt den Artikel dem Warenkorb hinzu. \\ \\ ===== 11.8Detailansicht ===== {{%5B2017-04-13%5D-ipa-bericht_html_bcacfd5a13438359.png?601x526}}**Abbildung 6: Detailansicht** Die Detailansicht öffnet sich nachdem in der Artikelliste auf dem entsprechenden Knopf gedrückt wurde. Hierbei wird die vollständige Beschreibung angezeigt im Gegensatz zur Artikelliste. Von der Detailansicht aus kann der entsprechende Artikel zum Warenkorb hinzugefügt werden. Es kann erst wieder mit der Artikelliste interagiert werden, nachdem die Detailansicht geschlossen wurde. Die Detailansicht ist also ein Modaldialog. Zum Schliessen der Detailansicht kann entweder auf das **X,** im rechten oberen Ecken, oder auf die Fläche ausserhalb der Detailansicht geklickt werden. \\ \\ ===== 11.9Warenkorb ===== {{%5B2017-04-13%5D-ipa-bericht_html_9324daa39c7d8840.png?601x129}}**Abbildung 7: Leerer Warenkorb** {{%5B2017-04-13%5D-ipa-bericht_html_a3b8c49a3430d7b2.png?601x176}}**Abbildung 8: Warenkorb mit Artikelpositionen** In der Artikelliste befindet sich ganz oben ein Knopf, durch den der Warenkorb angezeigt wird. Der Warenkorb kann sich in zwei Zuständen befinden. Entweder ist er leer (siehe Abbildung oben) und der Bestellen-Knopf bleibt deaktiviert oder er enthält Artikel und der Bestellen-Knopf ist dementsprechend aktiviert. Vom Warenkorb aus kann eine Warenkorbposition durch den Nutzer entfernt werden oder die Artikelanzahl verändert werden. Ausserdem wird der Warenkorb durch ein 15 Minütiges Sessiontimeout geleert. \\ \\ ===== 11.10Bestellformular ===== {{%5B2017-04-13%5D-ipa-bericht_html_e8b18493f9c50d43.png?601x504}}**Abbildung 9: Bestellformular** Wenn der Nutzer auf den Bestellen-Knopf klickt, erscheint ein Bestellformular, welches Pflichtfelder und Optionalfelder beinhaltet. Die Pflichtfelder sind jeweils mit einem * markiert. Der Nutzer wird auf leere Pflichtfelder und allgemeine Fehleingaben hingewiesen. Sind alle Felder korrekt ausgefüllt wird die Bestellung in die Datenbank persisitert. \\ \\ \\ \\ ====== 12Testphase ====== \\ \\ **Tabelle 6: Tests** | **Test Nr.** | **Bereich** | **Test** | **Beschreibung** | **Erwartetes Resultat** | **Effektives Resultat** | **Bestanden** | | 1 | Einstieg | Einstieg Webshop | Aufrufen der Webshop-URL\\ http://192.168.1.250/webshop | Der "Webshop" Knopf wird angezeigt. | wie erwartet | ja | | 2 | Artikelansicht | Artikelliste anzeigen | Der Nutzer klickt auf den "Webshop" Knopf | Die Artikelliste wird angezeigt. | wie erwartet | ja | | 3 | \\ | Artikelliste blättern | Der Nutzer klickt in der Artikelliste auf den Vorwärtsknopf, damit die nächsten zehn Artikel angezeigt werden. | Die nächsten zehn Artikel werden angezeigt. Auf der letzten Seite ist der Vorwärtsknopf disabled. | wie erwartet | ja | | 4 | \\ | \\ | Der Nutzer klickt in der Artikelliste auf den Zurückknopf, damit die vorherigen zehn Artikel angezeigt werden. | Die vorherigen zehn Artikel werden angezeigt. Auf der ersten Seite ist der Zurückknopf disabled. | wie erwartet | ja | | 5 | \\ | Detailansicht öffnen | Der Nutzer klickt auf den „Details ansehen“ Knopf, welcher dafür sorgt, dass sich die Detailansicht öffnet. | Die Detailansicht wird in einem modalen Dialog geöffnet | wie erwartet | ja | | 6 | \\ | Detailansicht schliessen | Der Nutzer klickt in der Detailansicht auf den Schliessknopf, um die Detailansicht zu schliessen. | Die Detailansicht wird geschlossen, der Nutzer befindet sich wieder auf der Artikelliste | wie erwartet | ja | | 7 | \\ | \\ | Der Nutzer klickt in der Detailansicht auf einen Bereich ausserhalb der Detailansicht in die Artikelliste, um die Detailansicht zu schliessen. | Die Detailansicht wird geschlossen, der Nutzer befindet sich wieder auf der Artikelliste | wie erwartet | ja | | 8 | \\ | Zum Warenkorb hinzufügen | Der Nutzer klickt in der Detailansicht auf den „Zum Warenkorb hinzufügen“ Knopf, um den Artikel dem Warenkorb hinzuzufügen. | Der Artikel wird dem Warenkorb hinzugefügt. Wenn der Artikel schon im Warenkorb ist, wird derselbe Artikel mit einer neuen Position hinzugefügt. | wie erwartet | ja | | 9 | \\ | \\ | Der Nutzer klickt in der Artikelliste auf den „Zum Warenkorb hinzufügen“ Knopf, um den Artikel dem Warenkorb hinzuzufügen. | Der Artikel wird dem Warenkorb hinzugefügt. Wenn der Artikel schon im Warenkorb ist, wird derselbe Artikel mit einer neuen Position hinzugefügt. | wie erwartet | ja | | 10 | \\ | Warenkorb anzeigen | Der Nutzer klickt in der Artikelliste auf den „Warenkorb anzeigen“ Knopf, um den Warenkorb anzuzeigen. | Der Warenkorb wird angezeigt\\ Warenkorb | wie erwartet | ja | | 11 | Warenkorb | Artikelanzahl ändern | Der Nutzer erhöht bzw. senkt die Artikelanzahl, somit wird das Subtotal und Total neu berechnet. | Das Subtotal und Total werden neu berechnet. Bei der Eingabe von 0 oder einem negativen Wert wird der vorherige Wert wiederhergestellt. Nachkommastellen bei der Eingabe werden abgeschnitten. | wie erwartet | ja | | 12 | \\ | Position löschen | Der Nutzer klickt auf den „Position entfernen“ Knopf, damit die Warenkorbposition entfernt wird. | Die Artikelposition wird entfernt, das Total wird neu berechnet. Wenn der Warenkorb leer ist, ist der "Bestellen" Knopf disabled. | wie erwartet | ja | | 13 | \\ | Bestellen | Der Nutzer klickt auf den „Bestellen“ Knopf, um das Bestellformular anzuzeigen. | Das Bestellformular wird angezeigt. | wie erwartet | ja | | 14 | Bestellformular | Bestellformular ausfüllen | Der Nutzer füllt das Bestellformular falsch aus und klickt auf den „Bestellung speichern“ Knopf.\\ Mögliche Falscheingaben:\\ - leere Pflichtfelder\\ - unplausible Namensangaben\\ - ungültige Telefonnummer\\ - ungültige E-Mail-Adresse\\ - unplausible Adressangaben | Die Bestellung wird nicht gespeichert und der Nutzer wird auf Falscheingaben hingewiesen. | wie erwartet | ja | | 15 | \\ | Bestellung speichern | Der Nutzer füllt das Bestellformular korrekt aus und klickt auf den „Bestellung speichern“ Knopf. | Die Bestellung wird gespeichert und der Nutzer wird darauf hingewiesen, dass seine Bestellung erfolgreich gespeichert wurde. | wie erwartet | ja | | 16 | \\ | Bestellung kontrollieren | Nach Speichern einer Bestellung muss diese auf der Datenbank geprüft werden | Bestellungs-Record auf der Datenbank enthält die Daten der Bestellung | wie erwartet | ja | | 17 | Integration | Webshop anzeigen | Nach dem auf der Homepage der „webshop“ Knopf gedrückt wurde, soll als erstes die Artikelliste angezeigt | Die Artikelliste wird dem Nutzer präsentiert | wie erwartet | ja | | 18 | Sonstiges | Timeout | Der Nutzer führt nach mehr als 15 Minuten eine beliebige Aktion aus. | Dem Nutzer wird die Artikelliste angezeigt. Warenkorb und Bestellformular sind leer. | - - Falls der Nutzer sich beim Timeout im Warenkorb befindet, wird die Artikelliste unterhalb des Warenkorbs angezeigt.\\ \\ -"Bestellung speichern" nach Ablauf des Timeouts wird nicht verhindert, es wird ein leerer "artikel" gespeichert | nein | \\ \\ \\ \\ ====== 13Persönliche Auswertung ====== ==== 13.1.1Reflexion ==== Im Verlaufe der IPA habe ich hin und wieder Fragen gehabt oder bin nicht weitergekommen. Anfangs fragte ich noch nach oder habe mir eine Zweit- und Drittmeinung eingeholt. Sobald es darum ging das geplante umzusetzen bzw. die Realisierungsphase startete, wollte ich alles alleine schaffen. Zugegebenermassen waren die Basisklassen und Interfaces aus einem vorherigen Projekt kopiert wurden und vom Fachvorgesetzen geschrieben, das habe ich aber in den Kommentaren des Quellcodes angemerkt. Trotzdem lag noch viel Arbeit vor mir, die ich wie schon gesagt alleine Stämmen wollte. Bei einem Problem habe ich die Lösung selbst gefunden oder im Internet nach einem Lösungsansatz gesucht. Die Sache ist die. Obwohl ich die Lösung selbst gefunden habe, wäre es doch oft zeitlich gesehen effizienter gewesen, den Fachvorgesetzen nach einem Ratschlag zu fragen, um das Problem von einem anderen Blickwinkel sehen zu können. Die meisten Probleme jedoch lagen einem Flüchtigkeits- oder Gedankenfehler zu Grunde. Im Realisierungsteil, war meine Vorgehensweise zuerst den Quellcode zu schreiben, diesen dann zu kommentieren und zu guter Letzt den Bericht zu erweitern. Für die Modultests hatte ich keinen Raum mehr, was nicht heisst dass ich gar nie überprüft habe, ob das Programmierte funktioniert oder nicht. Ich habe einfach kein Testframework verwendet oder versucht eins zu emulieren. \\ \\ ==== 13.1.2Erkenntnisse ==== Rückblickend kann ich sagen, dass ich viel zu viel Arbeit auf mich genommen habe und zu perfektionistisch war. Ich habe mich zu fest darauf fokussiert alles ohne Fremdhilfe zu schaffen und habe dabei Fremdhilfe mit Nachfragen und Ratschläge einholen gleichgesetzt. Das Resultat ist, dass ich mit weniger Punktzahlen bestehen würde, als ich hätte eigentlich erreichen können. Das legitimiere ich mir damit, dass es nie mein Ziel war eine sehr gute Note zu bekommen. Teilweise stimmt das auch. Ausserdem haben mich die zehn Tage der IPA sehr viel stärker mitgenommen als ich im Vorhinein gedacht habe. Doch das allerwichtigste was ich für mich aus diesen zehn Tage ziehen kann ist, und das mag sehr banal und trivial klingen, dass es keine Schande ist nachzufragen, dass es keine Schande ist manchmal festzustecken und dass ich meine Intuition weniger durch mein Ego streitig machen soll. \\ \\ ====== 14Quellenverzeichnis ====== ===== 14.1Internetquellen ===== ==== 14.1.1Allgemeine Quellen ==== * __[[http://stackoverflow.com/|http://stackoverflow.com/]]__ * __[[https://www.w3schools.com/|https://www.w3schools.com/]]__ * __[[https://www.wikipedia.org/|https://www.wikipedia.org/]]__ ==== 14.1.2Spezifische Quellen ==== - __[[https://snyk.io/blog/77-percent-of-sites-use-vulnerable-js-libraries/|https://snyk.io/blog/77-percent-of-sites-use-vulnerable-js-libraries/]]__ \\ \\ ====== 15Glossar ====== | **Abkürzung** | **Begriff** | **Erklärung** | | **-** | Warenkorb | Der Warenkorb beinhaltet Warenkorbpositionen. | | **-** | Warenkorbposition | Eine Warenkorbposition besteht aus der Anzahl, den Artikelnamen, Stückpreis und Subtotal | \\ \\ \\ \\ ====== Anhang ====== [[#_Toc481509606|Projektroot 1]] [[#_Toc481509607|config 1]] [[#_Toc481509608|Action 2]] [[#_Toc481509609|Js 2]] [[#_Toc481509610|AddToCartAction 2]] [[#_Toc481509611|CloseWidgetAction 5]] [[#_Toc481509612|EditCartPosition 7]] [[#_Toc481509613|LoadArticleListAction 10]] [[#_Toc481509614|LoadNextAction 13]] [[#_Toc481509615|LoadPreviousAction 16]] [[#_Toc481509616|RemoveCartPositionAction 18]] [[#_Toc481509617|SaveOrderAction 21]] [[#_Toc481509618|ShowArticleDetailsAction 24]] [[#_Toc481509619|ShowCartTableAction 27]] [[#_Toc481509620|ShowContactFormAction 30]] [[#_Toc481509621|PHP 33]] [[#_Toc481509622|AddToCartAction 33]] [[#_Toc481509623|CloseWidgetAction 34]] [[#_Toc481509624|EditCartPosition 35]] [[#_Toc481509625|LoadArticleListAction 36]] [[#_Toc481509626|LoadNextAction 37]] [[#_Toc481509627|LoadPreviousAction 38]] [[#_Toc481509628|RemoveCartPositionAction 39]] [[#_Toc481509629|SaveOrderAction 40]] [[#_Toc481509630|ShowArticleDetailsAction 51]] [[#_Toc481509631|ShowCartTableAction 52]] [[#_Toc481509632|ShowContactFormAction 52]] [[#_Toc481509633|Business 53]] [[#_Toc481509634|Entity 53]] [[#_Toc481509635|Artikel 53]] [[#_Toc481509636|Bestellung 54]] [[#_Toc481509637|CartPosition 55]] [[#_Toc481509638|Decorator 57]] [[#_Toc481509639|Main 57]] [[#_Toc481509640|Persistence 60]] [[#_Toc481509641|Database 60]] [[#_Toc481509642|Logging 69]] [[#_Toc481509643|Session 72]] [[#_Toc481509644|Presentation 80]] [[#_Toc481509645|Widget 80]] [[#_Toc481509646|ArticleDetailsWidget 80]] [[#_Toc481509647|ArticleListWidget 82]] [[#_Toc481509648|ButtonWidget 85]] [[#_Toc481509649|CartTableWidget 86]] [[#_Toc481509650|ContactFormWidget 88]] [[#_Toc481509651|DropdownWidget 94]] [[#_Toc481509652|ImageWidget 100]] [[#_Toc481509653|InputWidget 101]] [[#_Toc481509654|PaginationWidget 103]] \\ \\ \\ \\ ====== Projektroot ====== ===== config ===== POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% AddToCartAction.prototype.perform = function perform(event) { \\ \\ var productInformation = this.clickedJQueryElement .closest%%('%%.ProductInformation'); var productId = productInformation.data%%('%%entity'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: AddToCartAction.name, id: productId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% AddToCartAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { console.log%%('%%Add To Cart:',textStatus); console.log(data); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% AddToCartAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; \\ \\ \\ \\ ==== CloseWidgetAction ==== %%/**%% * ----------------------------------------------------------------------------- * CloseWidgetAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {CloseWidgetAction} %%*/%% var CloseWidgetAction = function CloseWidgetAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% CloseWidgetAction.prototype.perform = function perform(event) { \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: CloseWidgetAction.name }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% CloseWidgetAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var destroyable = this.clickedJQueryElement.closest%%('%%.destroyable'); destroyable.remove(); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% CloseWidgetAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== EditCartPosition ==== %%/**%% * ----------------------------------------------------------------------------- * EditCartPositionAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {EditCartPositionAction} * * @author b.lade %%*/%% var EditCartPositionAction = function EditCartPositionAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% EditCartPositionAction.prototype.perform = function perform(event) { var positionId = this.clickedJQueryElement .parents%%('%%tr') .data%%('%%position-id'); var amount = this.clickedJQueryElement.val(); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: EditCartPositionAction.name, id: positionId, amount: amount }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% EditCartPositionAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var tableContainer = this.clickedJQueryElement .closest%%('%%.CartTableWidgetContainer'); tableContainer.replaceWith(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% EditCartPositionAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== LoadArticleListAction ==== %%/**%% * ----------------------------------------------------------------------------- * LoadArticleListAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {LoadArticleListAction} * * @author b.lade %%*/%% var LoadArticleListAction = function LoadArticleListAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% LoadArticleListAction.prototype.perform = function perform(event) { \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: LoadArticleListAction.name }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% LoadArticleListAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var container = $%%('%%.container'); (this.clickedJQueryElement).prop%%('%%disabled', true); container.empty(); container.append(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% LoadArticleListAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== LoadNextAction ==== %%/**%% * ----------------------------------------------------------------------------- * LoadNextAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {LoadNextAction} * * @author b.lade %%*/%% var LoadNextAction = function LoadNextAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% LoadNextAction.prototype.perform = function perform(event) { \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: LoadNextAction.name }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% LoadNextAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var container = this.clickedJQueryElement.closest%%('%%.ArticleListWidget'); container.replaceWith(data); console.log%%('%%load next:',textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% LoadNextAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== LoadPreviousAction ==== %%/**%% * ----------------------------------------------------------------------------- * LoadPreviousAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {LoadPreviousAction} * * @author b.lade %%*/%% var LoadPreviousAction = function LoadPreviousAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% LoadPreviousAction.prototype.perform = function perform(event) { \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: LoadPreviousAction.name }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% LoadPreviousAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var container = this.clickedJQueryElement.closest%%('%%.ArticleListWidget'); container.replaceWith(data); console.log%%('%%load previous:', textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% LoadPreviousAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== RemoveCartPositionAction ==== %%/**%% * ----------------------------------------------------------------------------- * RemoveCartPositionAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {RemoveCartPositionAction} * * @author b.lade %%*/%% var RemoveCartPositionAction = function RemoveCartPositionAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.perform = function perform(event) { var positionId = this.clickedJQueryElement .parents%%('%%tr') .data%%('%%position-id'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: RemoveCartPositionAction.name, id: positionId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var tableContainer = this.clickedJQueryElement .closest%%('%%.CartTableWidgetContainer'); tableContainer.replaceWith(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== SaveOrderAction ==== %%/**%% * ----------------------------------------------------------------------------- * RemoveCartPositionAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {RemoveCartPositionAction} * * @author b.lade %%*/%% var RemoveCartPositionAction = function RemoveCartPositionAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.perform = function perform(event) { var positionId = this.clickedJQueryElement .parents%%('%%tr') .data%%('%%position-id'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: RemoveCartPositionAction.name, id: positionId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { var tableContainer = this.clickedJQueryElement .closest%%('%%.CartTableWidgetContainer'); tableContainer.replaceWith(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% RemoveCartPositionAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== ShowArticleDetailsAction ==== %%/**%% * ----------------------------------------------------------------------------- * ShowArticleDetailsAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {ShowArticleDetailsAction} * * @author b.lade %%*/%% var ShowArticleDetailsAction = function ShowArticleDetailsAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.perform = function perform(event) { \\ \\ var productInformation = this.clickedJQueryElement .closest%%('%%.ProductInformation'); var productId = productInformation.data%%('%%entity'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: ShowArticleDetailsAction.name, id: productId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { $%%('%%body').append(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== ShowCartTableAction ==== %%/**%% * ----------------------------------------------------------------------------- * ShowArticleDetailsAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {ShowArticleDetailsAction} * * @author b.lade %%*/%% var ShowArticleDetailsAction = function ShowArticleDetailsAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.perform = function perform(event) { \\ \\ var productInformation = this.clickedJQueryElement .closest%%('%%.ProductInformation'); var productId = productInformation.data%%('%%entity'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: ShowArticleDetailsAction.name, id: productId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { $%%('%%body').append(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ==== ShowContactFormAction ==== %%/**%% * ----------------------------------------------------------------------------- * ShowArticleDetailsAction Konstruktor * ----------------------------------------------------------------------------- * * * * @param {JQuery} clickedJQueryElement Das HTML Element, jenes geklickt wurde, * in einem JQuery Objekt verpackt. * * @returns {ShowArticleDetailsAction} * * @author b.lade %%*/%% var ShowArticleDetailsAction = function ShowArticleDetailsAction(clickedJQueryElement) { this.clickedJQueryElement = clickedJQueryElement; this.perform(); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * JavaScript Aktion ausführen * ----------------------------------------------------------------------------- * * Im Grunde genommen wird hier ein JQuery ajax POST request ausgeführt. * Bei Erfolg wird die handleSuccess() Methode aufgerufen. Bei Miss- * erfolg die handleFailure() Methode. Von Aktion zu Aktion wird * sich perform() nicht grossartig ändern. * * Da bei der Methode perform() sowieso immer ein POST request ge- * tätigt wird, wird statdessen der JQuery ajax() Funktion die Jquery * post() Funktion verwendet. Per PHP kann dann auf die POST * Parameter zugegriffen werden. PHP verarbeitet die Daten und gibt * HTML Code in string Form zurück, damit JQuery, wenn alles gut * gelaufen ist, das DOM entsprechend manipulieren kann. * * @param {Object} event click event Objekt * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.perform = function perform(event) { \\ \\ var productInformation = this.clickedJQueryElement .closest%%('%%.ProductInformation'); var productId = productInformation.data%%('%%entity'); \\ \\ var jqxhr = jQuery.post%%('%%index.php', { action: ShowArticleDetailsAction.name, id: productId }); \\ \\ jqxhr.done(this.handleSuccess.bind(this)); \\ \\ jqxhr.fail(this.handleFailure.bind(this)); \\ \\ }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Erfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolgreichem JQuery ajax post request wird dementsprechend die Me- * thode handleSuccess() aufgerufen. Die Implementation der Logik wird * von Aktion zu Aktion unterschiedlich ausfallen. * * @param %%{*%%} data * @param {string} textStatus * @param {jqXHR} jqXHR * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleSuccess = function handleSuccess(data, textStatus, jqXHR) { $%%('%%body').append(data); console.log(textStatus); }; \\ \\ \\ \\ %%/**%% * ----------------------------------------------------------------------------- * Misserfolg verarbeiten * ----------------------------------------------------------------------------- * * Bei einem erfolglosen JQuery ajax post request wird dementsprechend die Me- * thode handleFailure() aufgerufen. * * @param {jqXHR} jqXHR * @param {string} textStatus * @param {string} errorThrown * * @returns {undefined} %%*/%% ShowArticleDetailsAction.prototype.handleFailure = function handleFailure(jqXHR, textStatus, errorThrown) { console.log(errorThrown); }; ===== PHP ===== ==== AddToCartAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $id = filter_input(INPUT_POST, self::ID, FILTER_SANITIZE_STRING); $entity = Database::getInstance()->load($id); \\ \\ $cartPosition = [ CartPosition::ARTICLE_NUMBER => $entity->get(Entity::ID), CartPosition::AMOUNT => 1, CartPosition::PRICE => $entity->get(Artikel::PRICE) ]; CartPosition::setCartPosition($cartPosition); } \\ \\ } ==== CloseWidgetAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { echo 'performed'; } \\ \\ } ==== EditCartPosition ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { \\ \\ $id = filter_input(INPUT_POST, self::ID, FILTER_SANITIZE_STRING); $amount = filter_input(INPUT_POST, CartPosition::AMOUNT, FILTER_UNSAFE_RAW); \\ \\ $cartTable = new CartTableWidget(CartPosition::getAllCartPositions()); $cartTable->hideColumn(Entity::DELETED); $cartTable->hideColumn(Entity::ID); $cartTable->hideColumn(CartPosition::CART_POSITION); \\ \\ if (!is_numeric($amount)) { echo $cartTable; return; } \\ \\ if %%((%%int) $amount < 1) { echo $cartTable; return; } \\ \\ $currentPosition = CartPosition::getCartPosition($id); \\ \\ $currentPosition[CartPosition::AMOUNT] = (int) $amount; \\ \\ CartPosition::setCartPosition($currentPosition, (int) $id); \\ \\ $newCartTable = new CartTableWidget(CartPosition::getAllCartPositions()); $newCartTable->hideColumn(Entity::DELETED); $newCartTable->hideColumn(Entity::ID); $newCartTable->hideColumn(CartPosition::CART_POSITION); echo $newCartTable; } \\ \\ } ==== LoadArticleListAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { echo new ArticleListWidget(10); } \\ \\ } ==== LoadNextAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $rowCount = SessionHelper::get(Database::ROW_COUNT); $currentOffset = SessionHelper::get(Database::CURRENT_OFFSET); $nextOffset = SessionHelper::get(Database::NEXT_OFFSET); $filter = [Entity::DELETED => 'false']; $className = Artikel::class; $recordCount = Database::getInstance()->count($className, $filter); if($nextOffset >= $recordCount) { parent::perform(); exit(); } SessionHelper::set(Database::NEXT_OFFSET, $nextOffset + $rowCount); SessionHelper::set(Database::CURRENT_OFFSET, $currentOffset + $rowCount); parent::perform(); } \\ \\ } ==== LoadPreviousAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $rowCount = SessionHelper::get(Database::ROW_COUNT); $initialRowCount = SessionHelper::get(Database::INITIAL_ROW_COUNT); $currentOffset = SessionHelper::get(Database::CURRENT_OFFSET); $nextOffset = SessionHelper::get(Database::NEXT_OFFSET); \\ \\ if ($currentOffset <= $initialRowCount) { parent::perform(); exit(); } SessionHelper::set(Database::NEXT_OFFSET, $nextOffset - $rowCount); SessionHelper::set(Database::CURRENT_OFFSET, $currentOffset - $rowCount); parent::perform(); } \\ \\ } ==== RemoveCartPositionAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $id = filter_input(INPUT_POST, self::ID, FILTER_SANITIZE_STRING); CartPosition::removeCartPosition%%((%%int) $id); $cartTable = new CartTableWidget(CartPosition::getAllCartPositions()); $cartTable->hideColumn(Entity::DELETED); $cartTable->hideColumn(Entity::ID); $cartTable->hideColumn(CartPosition::CART_POSITION); echo $cartTable; } \\ \\ } ==== SaveOrderAction ==== true zurück, falls der String nur aus Buchstaben * besteht und false, wenn mindestens ein Zeichen kein Buchstabe ist. %%*/%% protected function hasOnlyLetters(string $string): bool { if (ctype_alpha($string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Ausschliesslich Ziffern * ------------------------------------------------------------------------- * * Überprüft, ob ein String nur aus Ziffern besteht. * * @param string $string Der zu überprüfende String * * @return bool gibt true zurück, falls der String nur aus Ziffern * besteht und false, wenn mindestens ein Zeichen keine Ziffer ist. %%*/%% protected function hasOnlyDigits(string $string): bool { if (ctype_digit($string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Korrekte Strasse * ------------------------------------------------------------------------- * * Überprüft, ob die angegebene Strasse mit dem regex Pattern übereinstimmt. * * @param string $string Der zu überprüfende String * @param bool $optional true wenn das das zu überprüfen Eingabefeld * optional ist. * * @return bool gibt true zurück, falls der String dem regx Pattern * übereinstimt oder der übergebene String leer ist und $optional * false ist. Gibt false zurück, wenn der String * nicht dem regex Pattern entspricht %%*/%% protected function isValidStreet(string $string, bool $optional = false) { \\ \\ if ($string == %%''%% && $optional) { return true; } \\ \\ $regex = %%'/%%^[A-Za-z][A-Za-z0-9-öäüÖÜÄßàâçéèêëîïôûùüÿñæœ .]{2,64}$/U'; \\ \\ if (preg_match($regex, $string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Korrekte Postleitzahl * ------------------------------------------------------------------------- * * Überprüft, ob die angegebene Postleitzahl mit dem regex Pattern überein- * stimmt. * * @param string $string Der zu überprüfende String * @param bool $optional true wenn das das zu überprüfen Eingabefeld * optional ist. * * @return bool gibt true zurück, falls der String dem regx Pattern * übereinstimt oder der übergebene String leer ist und $optional * false ist. Gibt false zurück, wenn der String * nicht dem regex Pattern entspricht %%*/%% protected function isValidZipCode(string $string, bool $optional = false) { \\ \\ if ($string == %%''%% && $optional) { return true; } \\ \\ $regex = %%'/%%^[0-9]{4,5}$/U'; \\ \\ if (preg_match($regex, $string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Korrekter Name bzw. Vorname * ------------------------------------------------------------------------- * * Überprüft, ob der angegebene Name bzw. Vorname mit dem regex Pattern über- * einstimmt. * * @param string $string Der zu überprüfende String * @param bool $optional true wenn das das zu überprüfen Eingabefeld * optional ist. * * @return bool gibt true zurück, falls der String dem regx Pattern * übereinstimt oder der übergebene String leer ist und $optional * false ist. Gibt false zurück, wenn der String * nicht dem regex Pattern entspricht %%*/%% protected function isValidName(string $string) { $regex = %%'/%%^[A-Za-z-öäüÖÜÄßàâçéèêëîïôûùüÿñæœ ]{2,64}$/U'; \\ \\ if (preg_match($regex, $string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Korrekter Ort * ------------------------------------------------------------------------- * * Überprüft, ob der angegebene Ort mit dem regex Pattern übereinstimmt. * * @param string $string Der zu überprüfende String * @param bool $optional true wenn das das zu überprüfen Eingabefeld * optional ist. * * @return bool gibt true zurück, falls der String dem regx Pattern * übereinstimt oder der übergebene String leer ist und $optional * false ist. Gibt false zurück, wenn der String * nicht dem regex Pattern entspricht %%*/%% protected function isValidCity(string $string, bool $optional = false) { \\ \\ if ($string == %%''%% && $optional) { return true; } \\ \\ $regex = %%'/%%^[A-Za-z-öäüÖÜÄßàâçéèêëîïôûùüÿñæœ .]{2,58}$/U'; \\ \\ if (preg_match($regex, $string)) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Korrekte Telefonnummer * ------------------------------------------------------------------------- * * Überprüft, ob die angegebene Telefonnummer mit einer 0 beginnt und voll- * ständig ist. Vorwahlen werden als nicht korrekt angenommen, da die über- * prüfung sich momentan auf schweizer Telefonnummenr bezieht * * @param string $string Der zu überprüfende String * @param bool $optional true wenn das das zu überprüfen Eingabefeld * optional ist. * * @return bool gibt true zurück, falls der String einer schweizer * Telefonnumer ohne Vorwahl entspricht oder der übergebene String leer * ist und $optional false ist. Gibt false zu- * rück, wenn der String nicht dem regex Pattern entspricht. %%*/%% protected function isValidPhoneNumber(string $string, bool $optional = false): bool { \\ \\ if ($string == %%''%% && $optional) { return true; } \\ \\ $substrResult = substr($string, 0, 1) == 0; \\ \\ $strippedSpaces = str_replace(" ", "", $string); $strlenResult = strlen($strippedSpaces) === 10; \\ \\ $onlyDigitsResult = $this->hasOnlyDigits($strippedSpaces); \\ \\ if ($onlyDigitsResult && $substrResult && $strlenResult) { return true; } \\ \\ return false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $requiredFields = json_decode(filter_input(INPUT_POST, self::REQUIRED_FIELDS, FILTER_UNSAFE_RAW), true); $optionalFields = json_decode(filter_input(INPUT_POST, self::OPTIONAL_FIELDS, FILTER_UNSAFE_RAW), true); \\ \\ $formOfAddress = array_shift($requiredFields); \\ \\ if ($this->requiredFieldsEmpty($requiredFields)) { echo 'Pflichtfelder müssen ausgefüllt werden '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidName($requiredFields[ucfirst(Bestellung::LASTNAME)])) { echo 'Der Nachname ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidName($requiredFields[ucfirst(Bestellung::FIRSTNAME)])) { echo 'Der Vorname ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!filter_var($requiredFields[ucfirst(Bestellung::EMAIL_ADDRESS)], FILTER_VALIDATE_EMAIL)) { echo 'Die E-Mail Adresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidStreet($requiredFields[ucfirst(Bestellung::STREET)])) { echo 'Die Strasse der Rechnungsadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidZipCode($requiredFields[ucfirst(Bestellung::ZIP_CODE)])) { echo 'Die Postleitzahl der Rechnungsadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidCity($requiredFields[ucfirst(Bestellung::CITY)])) { echo 'Der Ort der Rechnungsadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidPhoneNumber($optionalFields[ucfirst(Bestellung::PHONENUMBER)], true)) { echo 'Die Telefonnummer ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidStreet($optionalFields[ucfirst(Bestellung::STREET)], true)) { echo 'Die Strasse der Lieferadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidZipCode($optionalFields[ucfirst(Bestellung::ZIP_CODE)], true)) { echo 'Die Postleitzahl der Lieferadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ if (!$this->isValidCity($optionalFields[ucfirst(Bestellung::CITY)], true)) { echo 'Der Ort der Lieferadresse ist nicht gültig. '; echo new ContactFormWidget(); return; } \\ \\ $order = new Bestellung(); \\ \\ $cartPositions = CartPosition::getAllCartPositions(); \\ \\ $articles = %%''%%; \\ \\ foreach ($cartPositions as $key => $cartPosition) { $articles .= $key + 1 . ';'; foreach ($cartPosition as $positionInfoKey => $positionInfo) { if ($positionInfoKey === CartPosition::ARTICLE_NUMBER) { $id = $positionInfo; $entity = Database::getInstance()->load($id); $name = $entity->get(Artikel::TITLE); $articles .= $name . ';'; } if ($positionInfoKey === CartPosition::AMOUNT) { $articles .= $positionInfo . ';'; } if ($positionInfoKey === CartPosition::PRICE) { $articles .= $positionInfo . '\n\r'; } } } \\ \\ $orderData = [ Bestellung::ORDER_DATE => time(), Bestellung::LASTNAME => $requiredFields[ucfirst(Bestellung::LASTNAME)], Bestellung::EMAIL => $requiredFields[ucfirst(Bestellung::EMAIL_ADDRESS)], Bestellung::PHONENUMBER => $optionalFields[ucfirst(Bestellung::PHONENUMBER)], Bestellung::ADDRESS => $formOfAddress . ' ' . $requiredFields[ucfirst(Bestellung::LASTNAME)] . ' ' . $requiredFields[ucfirst(Bestellung::FIRSTNAME)] . '\r\n' . ' ' . $requiredFields[ucfirst(Bestellung::STREET)] . '\r\n' . ' ' . $requiredFields[ucfirst(Bestellung::ZIP_CODE)] . ' ' . $requiredFields[ucfirst(Bestellung::CITY)] . '\r\n' . 'schweiz \r\n', Bestellung::SHIPPING_ADDRESS => $formOfAddress . ' ' . $requiredFields[ucfirst(Bestellung::LASTNAME)] . ' ' . $requiredFields[ucfirst(Bestellung::FIRSTNAME)] . '\r\n' . ' ' . $optionalFields[ucfirst(Bestellung::STREET)] . '\r\n' . ' ' . $optionalFields[ucfirst(Bestellung::ZIP_CODE)] . ' ' . $optionalFields[ucfirst(Bestellung::CITY)] . '\r\n' . 'schweiz \r\n', Bestellung::ARTICLE => $articles ]; \\ \\ $order->setData($orderData); \\ \\ Database::getInstance()->save($order); \\ \\ echo 'Bestellung gespeichert '; \\ \\ exit(); } \\ \\ } ==== ShowArticleDetailsAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $productId = filter_input(INPUT_POST, Action::ID, FILTER_SANITIZE_STRING); $entity = Database::getInstance()->load($productId); $fields = $entity->getFields(Artikel::class); \\ \\ echo new ArticleDetailsWidget($entity, $fields); } \\ \\ } ==== ShowCartTableAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $productId = filter_input(INPUT_POST, Action::ID, FILTER_SANITIZE_STRING); $entity = Database::getInstance()->load($productId); $fields = $entity->getFields(Artikel::class); \\ \\ echo new ArticleDetailsWidget($entity, $fields); } \\ \\ } ==== ShowContactFormAction ==== perform() Implementation * ------------------------------------------------------------------------- * * @see ActionInterface->perform() Interface implementation von perform() * %%*/%% public function perform() { $productId = filter_input(INPUT_POST, Action::ID, FILTER_SANITIZE_STRING); $entity = Database::getInstance()->load($productId); $fields = $entity->getFields(Artikel::class); \\ \\ echo new ArticleDetailsWidget($entity, $fields); } \\ \\ } ====== Business ====== ===== Entity ===== ==== Artikel ==== connection Variable zugewiesen. Falls ein Fehler * beim Verbinden mit der Datenbank auftritt, wird eine Fehlermeldung im * Browser ausgegeben. * * @return void %%*/%% protected function connect() { $dataSourceName = self::DRIVER . ":dbname=" . self::SCHEMA . ";host=" . self::HOST . ";charset=utf8"; \\ \\ if ($this->connection === null) { try { $this->connection = new PDO( $dataSourceName, self::USER, self::PASSWORD ); \\ \\ $this->connection->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); } catch (PDOException $exception) { echo 'Datenbankfehler D001
' . $exception; Log::logError%%('%%Datenbankfehler D001', $exception, $dataSourceName); exit(); } } } \\ \\ %%/**%% * ------------------------------------------------------------------------- * SQL insert statement * ------------------------------------------------------------------------- * * Hier wird ein insert statement aufgebaut und ausgeführt. Falls ein Fehler * beim insert statement auftritt, wird eine entsprechende Fehlermeldung * im Browser ausgegeben. * * @param \Entity $entity Enthält alle Informationen welche in die Datenbank * persistiert werden sollen * @return void %%*/%% private function insert(Entity $entity) { \\ \\ $data = $entity->getData(); \\ \\ $sql = "insert into " . get_class($entity); $sqlColumn = " ("; $sqlValues = " ("; $values = []; \\ \\ foreach ($data as $fieldName => $fieldValue) { \\ \\ $sqlColumn .= $fieldName . ", "; \\ \\ $sqlValues .= "?, "; \\ \\ $values[] = $fieldValue; } \\ \\ $sqlColumn = rtrim($sqlColumn, ', '); $sqlValues = rtrim($sqlValues, ', '); \\ \\ $sql .= $sqlColumn . ") values " . $sqlValues . ")"; \\ \\ try { $this->connect(); $statement = $this->connection->prepare($sql); $statement->execute($values); } catch (PDOException $e) { echo 'Datenbankfehler D002'; Log::logError%%('%%Datenbankfehler D002', $exception, $statement->queryString); exit(); } } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Instanzierung des Database Objekts * ------------------------------------------------------------------------- * * Die Database Klasse folgt dem Singeltonpattern. Wärend der Laufzeit wird * sichergestellt, dass nur eine Instanz der Klasse Database existiert. * * @return \Database Gibt eine Instanz der Klasse Database zurück. %%*/%% public static function getInstance(): Database { if (self::$db === null) { self::$db = new Database(); } return self::$db; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Anzahl der Tabellenreihen * ------------------------------------------------------------------------- * * Hier wird die Anzahl der Reihen in einer Tabelle zurückgegeben. Bei einem * Fehler wird im Browser eine entsprechende Fehlermeldung ausgegeben. * * @param string $className Entspricht dem Tabellenname in der Datenbank * @param array $filter Liest nur die Daten aus der Datenbank aus welche im * Filter angegeben wurden. * @return int Liefert die anzahl der Tabellenreihe zurück %%*/%% public function count(string $className, array $filter = null): int { $filterSql = %%''%%; \\ \\ if ($filter !== null) { $filterSql = ' where '; foreach ($filter as $key => $f) { $filterSql .= $key; $filterSql .= '='; $filterSql .= '\%%''%% . $f . '\' and '; } $filterSql = rtrim($filterSql, ' and '); } \\ \\ $class = strtolower($className); \\ \\ $sql = 'select count%%(*%%) from ' . $class . ' ' . $filterSql; \\ \\ try { $this->connect(); $statement = $this->connection->prepare($sql); $statement->execute(); $result = $statement->fetch(PDO::FETCH_NUM); } catch (PDOException $exception) { echo 'Datenbankfehler D003'; Log::logError%%('%%Datenbankfehler D003', $exception, $statement->queryString); exit(); } \\ \\ $rowCount = (int) $result[0]; \\ \\ return $rowCount; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Datensatz aus Datenbank lesen * ------------------------------------------------------------------------- * * Hier wird nur ein Datensatz der der Id entspricht zurückgeliefert * * @todo Kommentar anpassen * * @param string $id Entspricht der Id aus der Tabelle in der Datenbank * @return \Entity Liefert ein Entity Objekt zurück %%*/%% public function load(string $id) { $dataType = explode(self::ID_SEPERATOR, $id)[0]; \\ \\ $this->connect(); try { $sql = 'select * from ' . strtolower($dataType) . ' WHERE id=?'; \\ \\ $statement = $this->connection->prepare($sql); \\ \\ $statement->execute%%([%%$id]); \\ \\ $result = $statement->fetch(PDO::FETCH_ASSOC); \\ \\ if (!$result) { return null; } $entity = new $dataType(); \\ \\ \\ \\ $entity->setData($result); \\ \\ return $entity; } catch (PDOException $exception) { echo 'Datenbankfehler D004'; Log::logError%%('%%Datenbankfehler D004', $exception, $statement->queryString); exit(); } } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Datensätze aus Datenbank auslesen * ------------------------------------------------------------------------- * * Hier werden mehre bzw. alle Datensätze aus der Datenbank rausgelesen und * und in einer Liste zurückgegeben. * * @todo loadList Parameter + limit validieren und 'säubern' * * @param string $className Entspricht dem Tabellenname in der Datenbank * @param array $filter Liest nur die Daten aus der Datenbank aus welche im * Filter angegeben wurden. * @return array Liefert eine Liste mit Entity Objekten zurück %%*/%% public function loadList(string $className, array $filter = null): array { $filterSql = %%''%%; \\ \\ if ($filter !== null) { $filterSql = ' WHERE '; foreach ($filter as $key => $f) { $filterSql .= $key; $filterSql .= '='; $filterSql .= '\%%''%% . $f . '\' and '; } $filterSql = rtrim($filterSql, ' and '); } \\ \\ $class = strtolower($className); \\ \\ $start = SessionHelper::get(self::CURRENT_OFFSET); $amount = SessionHelper::get(self::ROW_COUNT); \\ \\ $limit = ' LIMIT ' . $start . ',' . $amount; \\ \\ $statement = %%''%%; \\ \\ $result = %%''%%; \\ \\ $this->connect(); try { $statement = $this->connection->prepare%%('%%select * from ' . $class . ' ' . $filterSql . $limit); \\ \\ $statement->execute(); \\ \\ $result = $statement->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $exception) { echo 'Datenbankfehler D005'; Log::logError%%('%%Datenbankfehler D005', $exception, $statement->queryString); exit(); } \\ \\ $returnVal = []; \\ \\ foreach ($result as $data) { \\ \\ $entity = new $className(); \\ \\ $entity->setData($data); \\ \\ $returnVal[] = $entity; } \\ \\ return $returnVal; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Daten persistieren * ------------------------------------------------------------------------- * * Momentan ist die save() Methode nur ein Wrapper um die insert() * Methode. Nach der IPA wird die von save() Funktionalität erweitert. * * @see Database::insert() * * @param Entity $entity Wird direkt an %%*/%% public function save(Entity $entity) { $this->insert($entity); } \\ \\ } ===== Logging ===== $propertyValue) { $message .= "{$propertyName}={$propertyValue};"; } self::writeLog($message); } } \\ \\ %%//%% logge eine Fehlermeldung %%/**%% * ------------------------------------------------------------------------- * Fehlermeldung loggen * ------------------------------------------------------------------------- * * Loggt eine Fehlermeldung. * * @param string $error Fehlermeldung * @param string $stack Stacktrace * @param string $optionalInfo optionale Information * * @return void %%*/%% public static function logError(string $error, string $stack, string $optionalInfo = %%''%%) { $message = "#ERROR: {$error}\ninfo1: {$stack}\ninfo2: {$optionalInfo}"; self::writeLog($message); } \\ \\ } ===== Session ===== SessionHelper wird das Singleton Pattern * verwendet, damit sichergestellt wird, dass nur eine Instanz dieser * Klasse existiert. * * Ausserdem ist die Methode getInstance protected, * damit sie nicht von aussen aus aufgerufen werden kann. * * @return SessionHelper Gibt eine Instanz der Klasse SessionHelper * zurück. %%*/%% protected static function getInstance(): SessionHelper { if (self::$sessionHelper === null) { self::$sessionHelper = new SessionHelper(); } \\ \\ return self::$sessionHelper; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Session starten * ------------------------------------------------------------------------- * * Die Methode start startet eine Session, falls sie vorher * noch nicht gestartet wurde. * * Ausserdem ist die Methode start protected, * damit sie nicht von aussen aus aufgerufen werden kann, da sie nur * innerhalb der Klasse SessionHelper verwendet wird. * * @return void %%*/%% protected static function start() { if (static::getSessionStatus() !== PHP_SESSION_ACTIVE) { session_start(); } } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessiontimeoutlänge setzen * ------------------------------------------------------------------------- * * @param int $timeout * * @return void %%*/%% public static function setTimeout(int $timeout) { $this->timeout = $timeout; $this->timeoutInactivity = false; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Session immernoch aktiv * ------------------------------------------------------------------------- * * Überprüfen ob die Session immernoch aktiv bzw. noch nicht abgelaufen ist. * Wenn ja, wird true zurückgegeben. Sonst, wird false * zurückgegeben. * * @return bool %%*/%% public static function isActive(): bool { self::getInstance()->start(); if (self::$timeoutInactivity) { \\ \\ if (!is_int(self::get(self::TIMEOUT))) { self::set(self::TIMEOUT, time()); } %%/*%% * Unterschied in Sekunden %%*/%% $lastActionDelta = self::get(self::TIMEOUT) - time(); \\ \\ if ($lastActionDelta < 0) { return false; } \\ \\ self::newSession(); } \\ \\ return true; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessionstatus * ------------------------------------------------------------------------- * * @return mixed %%*/%% public static function getSessionStatus() { return session_status(); } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessionvariable array * ------------------------------------------------------------------------- * * Die Methode getArray, gibt den Inhalt einer Sessionvariable * zurück, welcher ein array ist. * * @param string $fieldName Der Feldname, um auf die entsprechenden * Sessionvariable zugreifen zu können. * @param int $arrayIndex * * @return array Gibt den Inhalt einer Sessionvariable * zurück, welcher ein array ist. Wenn die Sessionvariable nicht * existiert, wird ein leerer string zurückgegeben. %%*/%% public static function getArray(string $fieldName, int $arrayIndex = null): array { self::getInstance()->start(); if (!isset($_SESSION[$fieldName])) { return []; } %%//%% if ($arrayIndex !== null) { return $_SESSION[$fieldName][$arrayIndex]; } return $_SESSION[$fieldName]; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessionvaribale Inhalt * ------------------------------------------------------------------------- * * Die Methode get, gibt den Inhalt einer Sessionvariable * zurück. * * @param string $fieldName Der Feldname, um auf die entsprechenden * Sessionvariable zugreifen zu können. * @return mixed Gibt den Inhalt einer Sessionvariable * zurück. Wenn die Sessionvariable nicht * existiert, wird ein leerer string zurückgegeben. %%*/%% public static function get(string $fieldName) { self::getInstance()->start(); if (!isset($_SESSION[$fieldName])) { return %%''%%; } return $_SESSION[$fieldName]; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessionvariable array registrieren * ------------------------------------------------------------------------- * * Die Methode setArray, registriert eine Sessionvariable, * deren Inhalt ein array ist. * * @param string $fieldName Der Feldname, um auf die entsprechenden * Sessionvariable zugreifen zu können. * @param array $value Das array, welches als Wert der * Sessionvariable zugewiesen werden soll. * @param int $arrayIndex * * @return void %%*/%% public static function setArray(string $fieldName, array $value, int $arrayIndex = null) { self::getInstance()->start(); if ($arrayIndex !== null) { $_SESSION[$fieldName][$arrayIndex] = $value; return; } else if ($fieldName === CartPosition::CART_POSITION) { $_SESSION[$fieldName][] = $value; return; } $_SESSION[$fieldName] = $value; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessionvariable Inhalt registrieren * ------------------------------------------------------------------------- * * Die Methode set, registriert eine Sessionvarible. * * @param string $fieldName Der Feldname, um auf die entsprechenden * Sessionvariable zugreifen zu können. * @param void $value Der string, welcher als Wert der * Sessionvariable zugewiesen werden soll. * * @return void %%*/%% public static function set(string $fieldName, $value) { self::getInstance()->start(); $_SESSION[$fieldName] = $value; } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Warenkorbposition entfernen * ------------------------------------------------------------------------- * * Die Warenkorbposition wird entsprechend der Variable cartPositionIndex * entfernt. * * @param int $cartPositionIndex * @param string $fieldName * * @return void %%*/%% public static function destroyCartPosition(int $cartPositionIndex) { self::getInstance()->start(); %%//%% Warenkorb Position aus der Session Variablen 'cartPosition' entfernen unset($_SESSION[CartPosition::CART_POSITION][$cartPositionIndex]); %%//%% Session Variable 'cartPosition' neu indexieren $_SESSION[CartPosition::CART_POSITION] = array_values($_SESSION[CartPosition::CART_POSITION]); } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Session beenden * ------------------------------------------------------------------------- * * Die Methode %%%%destroy zerstört bzw. beendet eine Session. * * @return void %%*/%% public static function destroy() { self::getInstance()->start(); session_unset(); session_destroy(); } \\ \\ %%/**%% * ------------------------------------------------------------------------- * Sessiontimeout starten * ------------------------------------------------------------------------- * * @return void %%*/%% public static function newSession() { self::getInstance()->start(); $_SESSION[self::TIMEOUT] = time() + self::$timeout; } \\ \\ } ====== Presentation ====== ===== Widget ===== ==== ArticleDetailsWidget ==== entity = $entity; $this->fields = $fields; } public function prepare() { echo '
'; echo ''; echo '
'; } } ==== ArticleListWidget ==== itemsPerPage = $itemsPerPage; } \\ \\ protected function prepareListItems($fieldNames, $entity) { foreach ($fieldNames as $fieldName) { echo '
  • '; switch ($fieldName) { case Artikel::DESCRIPTION: $descriptionLength = strlen($entity->get($fieldName)); if ($descriptionLength > 200) { $shortDescription = substr($entity->get($fieldName), 0, 200) . '…'; echo $fieldName . ': ' . $shortDescription; } else { echo $fieldName . ': ' . $entity->get($fieldName); } break; case Artikel::PRICE: echo $fieldName . ': ' . $entity->get($fieldName) . '.-'; break; case Artikel::IMAGE: $imageContent = $entity->get($fieldName); $encodedImageContent = base64_encode($imageContent); $imageMimeType = getimagesizefromstring($encodedImageContent)%%['%%mime']; $src = 'data:' . $imageMimeType . ';base64,' . $encodedImageContent; $alt = $entity->get(Artikel::TITLE); $imageWidget = new ImageWidget($src, $alt); $imageWidget->addCssClass%%('%%product-image'); echo $imageWidget; break; default: echo $fieldName . ': ' . $entity->get($fieldName); break; } echo '
  • '; } $viewArticleButton = new ButtonWidget%%('%%Detailansicht anzeigen'); $viewArticleButton->setActionName(ShowArticleDetailsAction::class); $addToCartButton = new ButtonWidget%%('%%zum Warenkorb hinzufügen'); $addToCartButton->setActionName(AddToCartAction::class); echo '
  • '; echo $viewArticleButton; echo $addToCartButton; echo '
  • '; } \\ \\ protected function prepareUnorderedList($productList, $fieldNames) { foreach ($productList as $entity) { echo '
      '; $this->prepareListItems($fieldNames, $entity); echo '
    '; } } \\ \\ public function prepare() { $productFilter = [ Entity::DELETED => 'false' ]; \\ \\ $productList = Database::getInstance()->loadList(Artikel::class, $productFilter); $fieldNames = Entity::getFields(Artikel::class); \\ \\ echo '
    '; $showCartTableButton = new ButtonWidget%%('%%Warenkorb anzeigen'); $showCartTableButton->setActionName(ShowCartTableAction::class); echo $showCartTableButton; echo '

    Produktliste

    '; $this->prepareUnorderedList($productList, $fieldNames); echo new PaginationWidget($productFilter, $this->itemsPerPage); echo '
    '; } \\ \\ } ==== ButtonWidget ==== setLabel($label); } \\ \\ public function setDisabled($isDisabled) { $this->isDisabled = $isDisabled; } public function prepare() { $this->code = ''; } } ==== CartTableWidget ==== cartPostions = $cartPositions; } \\ \\ %%/**%% * @todo edit comment %%*/%% public function hideColumn($columnName) { $this->hiddenColumns[$columnName] = true; } \\ \\ public function prepare() { echo '
    '; $backToProductListButton = new ButtonWidget%%('%%Produkliste anzeigen'); $backToProductListButton->setActionName(LoadArticleListAction::class); echo $backToProductListButton; echo '

    Warenkorb

    '; echo ''; echo ''; echo ''; $fieldNames = Entity::getFields(CartPosition::class); foreach ($fieldNames as $fieldName) { if (!isset($this->hiddenColumns[$fieldName])) { echo ''; } } echo ''; echo ''; echo ''; $total = 0; foreach ($this->cartPostions as $key => $cartPosition) { $subTotal = $cartPosition[CartPosition::PRICE] * $cartPosition[CartPosition::AMOUNT]; $total += $subTotal; echo ''; $artikel = Database::getInstance()->load($cartPosition[CartPosition::ARTICLE_NUMBER]); $amountInputWidget = new InputWidget%%(''%%, $cartPosition[CartPosition::AMOUNT], InputWidget::NUMBER); $amountInputWidget->setActionName(EditCartPositionAction::class); $amountInputWidget->addCssClass(InputWidget::class . 'Amount'); $amountInputWidget->addHtmlAttribute%%('%%min="1"'); $amountInputWidget->addHtmlAttribute%%('%%pattern="[0-9]"'); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo ''; echo ''; echo ''; echo ''; echo '
    ' . $fieldName . '
    ' . $amountInputWidget . '' . $artikel->get(Artikel::TITLE) . '' . $cartPosition[CartPosition::PRICE] . '' . $subTotal . ''; $removeButton = new ButtonWidget%%('%%Position entfernen'); $removeButton->setActionName(RemoveCartPositionAction::class); $removeButton->addCssClass(ButtonWidget::class . 'Small'); echo $removeButton; echo '
    Total ' . $total . '
    '; $orderButton = new ButtonWidget%%('%%Bestellen'); $orderButton->setActionName(ShowContactFormAction::class); if (count($this->cartPostions) === 0 ) { $orderButton->setDisabled(true); } echo $orderButton; echo '
    '; } } ==== ContactFormWidget ==== isRequired(true); %%/**%% * --------------------------------------------------------------------- * Nachname des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Mustermann * * @var \InputWidget %%*/%% $lastName = new InputWidget(ucfirst(Bestellung::LASTNAME)); $lastName->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Vorname des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Hans * * @var \InputWidget %%*/%% $firstName = new InputWidget(ucfirst(Bestellung::FIRSTNAME)); $firstName->isRequired(true); %%/**%% * --------------------------------------------------------------------- * E-Mail-Adresse des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: hans.mustermann@email.com * * @var \InputWidget %%*/%% $emailAddress = new InputWidget(ucfirst(Bestellung::EMAIL_ADDRESS), %%''%%, InputWidget::EMAIL); $emailAddress->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Telefonnummer des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: 056 222 22 22 * * @var \InputWidget %%*/%% $phoneNumber = new InputWidget(ucfirst(Bestellung::PHONENUMBER), %%''%%, InputWidget::TEL); %%/**%% * --------------------------------------------------------------------- * Strasse der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Mustermannstrasse 444 * * @var \InputWidget %%*/%% $street = new InputWidget(ucfirst(Bestellung::STREET)); $street->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Strasse der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: Mustermannstrasse 444 * * @var \InputWidget %%*/%% $shippingStreet = new InputWidget(ucfirst(Bestellung::STREET)); \\ \\ %%/**%% * --------------------------------------------------------------------- * PLZ der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: 8957 * * @var \InputWidget %%*/%% $zipCode = new InputWidget(ucfirst(Bestellung::ZIP_CODE)); $zipCode->isRequired(true); %%/**%% * --------------------------------------------------------------------- * PLZ der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: 8957 * * @var \InputWidget %%*/%% $shippingZipCode = new InputWidget(ucfirst(Bestellung::ZIP_CODE)); %%/**%% * --------------------------------------------------------------------- * Ort der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Aargau * * @var \InputWidget %%*/%% $city = new InputWidget(ucfirst(Bestellung::CITY)); $city->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Ort der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: Aargau * * @var \InputWidget %%*/%% $shippingCity = new InputWidget(ucfirst(Bestellung::CITY)); %%/**%% * --------------------------------------------------------------------- * Vorname Eingabefeld * --------------------------------------------------------------------- * * Dieses Formularsteuerelement setzt den Speichervorgang der Bestellung * in gang. * * @var \ButtonWidget %%*/%% $confirmOrder = new ButtonWidget%%('%%Bestellung speichern'); $confirmOrder->setActionName(SaveOrderAction::class); echo '
    '; echo '

    Bestellformular

    '; echo '
    '; echo 'Name'; echo "{$formOfAddress} {$lastName}
    {$firstName}"; echo '
    '; \\ \\ echo '
    '; echo 'Email / Telefon'; echo "{$emailAddress}
    {$phoneNumber}"; echo '
    '; echo '
    '; echo 'Rechnungsadresse'; echo "{$street}
    {$zipCode} {$city}"; echo '
    '; echo '
    '; echo 'Lieferadresse'; echo "{$shippingStreet}
    {$shippingZipCode} {$shippingCity}"; echo '
    '; echo '

    Pflichtfelder sind mit * markiert.

    '; \\ \\ echo $confirmOrder; echo '
    '; } } ==== DropdownWidget ==== isRequired(true); %%/**%% * --------------------------------------------------------------------- * Nachname des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Mustermann * * @var \InputWidget %%*/%% $lastName = new InputWidget(ucfirst(Bestellung::LASTNAME)); $lastName->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Vorname des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Hans * * @var \InputWidget %%*/%% $firstName = new InputWidget(ucfirst(Bestellung::FIRSTNAME)); $firstName->isRequired(true); %%/**%% * --------------------------------------------------------------------- * E-Mail-Adresse des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: hans.mustermann@email.com * * @var \InputWidget %%*/%% $emailAddress = new InputWidget(ucfirst(Bestellung::EMAIL_ADDRESS), %%''%%, InputWidget::EMAIL); $emailAddress->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Telefonnummer des Bestellers * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: 056 222 22 22 * * @var \InputWidget %%*/%% $phoneNumber = new InputWidget(ucfirst(Bestellung::PHONENUMBER), %%''%%, InputWidget::TEL); %%/**%% * --------------------------------------------------------------------- * Strasse der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Mustermannstrasse 444 * * @var \InputWidget %%*/%% $street = new InputWidget(ucfirst(Bestellung::STREET)); $street->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Strasse der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: Mustermannstrasse 444 * * @var \InputWidget %%*/%% $shippingStreet = new InputWidget(ucfirst(Bestellung::STREET)); \\ \\ %%/**%% * --------------------------------------------------------------------- * PLZ der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: 8957 * * @var \InputWidget %%*/%% $zipCode = new InputWidget(ucfirst(Bestellung::ZIP_CODE)); $zipCode->isRequired(true); %%/**%% * --------------------------------------------------------------------- * PLZ der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: 8957 * * @var \InputWidget %%*/%% $shippingZipCode = new InputWidget(ucfirst(Bestellung::ZIP_CODE)); %%/**%% * --------------------------------------------------------------------- * Ort der Rechnungsadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist ein Pflichtfeld. * * Beispiel: Aargau * * @var \InputWidget %%*/%% $city = new InputWidget(ucfirst(Bestellung::CITY)); $city->isRequired(true); %%/**%% * --------------------------------------------------------------------- * Ort der Lieferadresse * --------------------------------------------------------------------- * * Dieses Formularsteuerelement ist kein Pflichtfeld. * * Beispiel: Aargau * * @var \InputWidget %%*/%% $shippingCity = new InputWidget(ucfirst(Bestellung::CITY)); %%/**%% * --------------------------------------------------------------------- * Vorname Eingabefeld * --------------------------------------------------------------------- * * Dieses Formularsteuerelement setzt den Speichervorgang der Bestellung * in gang. * * @var \ButtonWidget %%*/%% $confirmOrder = new ButtonWidget%%('%%Bestellung speichern'); $confirmOrder->setActionName(SaveOrderAction::class); echo '
    '; echo '

    Bestellformular

    '; echo '
    '; echo 'Name'; echo "{$formOfAddress} {$lastName}
    {$firstName}"; echo '
    '; \\ \\ echo '
    '; echo 'Email / Telefon'; echo "{$emailAddress}
    {$phoneNumber}"; echo '
    '; echo '
    '; echo 'Rechnungsadresse'; echo "{$street}
    {$zipCode} {$city}"; echo '
    '; echo '
    '; echo 'Lieferadresse'; echo "{$shippingStreet}
    {$shippingZipCode} {$shippingCity}"; echo '
    '; echo '

    Pflichtfelder sind mit * markiert.

    '; \\ \\ echo $confirmOrder; echo '
    '; } } ==== ImageWidget ==== src = $src; $this->alt = $alt; } \\ \\ public function prepare() { echo 'alt !== %%''%% ? 'alt="'. $this->alt .'"' : %%''%%) . %%'/%%>'; } } ==== InputWidget ==== setLabel($label); $this->setValue($value); $this->type = $type; } public function isRequired(bool $isRequired) { $this->isRequired = $isRequired; } public function addHtmlAttribute(string $htmlAttribute) { if ($this->htmlAttributes === null) { $this->htmlAttributes = ' ' . $htmlAttribute; return; } $this->htmlAttributes .= ' ' . $htmlAttribute; } \\ \\ public function prepare() { $this->code = ''; $this->code .= 'label !== %%''%% ? 'name="' . $this->label . '" ' : %%''%% ) . 'class="' . self::class . $this->cssClasses . ucfirst($this->label) . '" ' . 'id="' . $this->id . '" ' . 'type="' . $this->type . '" ' . $this->htmlAttributes . ' ' . ($this->actionName !== %%''%% ? 'data-action="' . $this->actionName . '" ': %%''%%) . ($this->isRequired ? 'required' : %%''%%) . ' ' . 'value="' . $this->value . '"/>'; } } ==== PaginationWidget ==== filter = $filter; $this->itemsPerPage = $itemsPerPage; } \\ \\ public function prepare() { $loadPreviousButton = new ButtonWidget%%('%%load previous'); $loadPreviousButton->setActionName(LoadPreviousAction::class); if (SessionHelper::get(Database::CURRENT_OFFSET) <= Database::INITIAL_ROW_COUNT) { $loadPreviousButton->setDisabled(true); } \\ \\ $loadMoreButton = new ButtonWidget%%('%%load next'); $loadMoreButton->setActionName(LoadNextAction::class); if (SessionHelper::get(Database::NEXT_OFFSET) >= Database::getInstance()->count(Artikel::class, $this->filter)) { $loadMoreButton->setDisabled(true); } \\ \\ echo '
    '; echo $loadPreviousButton; echo $loadMoreButton; echo '
    '; } } [[#sdfootnote1anc|1]] Vergangene Stunden am heutigen Tag [[#sdfootnote2anc|2]] Insgesamt vergangene Stunden [[#sdfootnote3anc|3]] Im Projekt verbleibende Stunden [[#sdfootnote4anc|4]] Vergangene Stunden am heutigen Tag [[#sdfootnote5anc|5]] Insgesamt vergangene Stunden [[#sdfootnote6anc|6]] Im Projekt verbleibende Stunden [[#sdfootnote7anc|7]] Vergangene Stunden am heutigen Tag [[#sdfootnote8anc|8]] Insgesamt vergangene Stunden [[#sdfootnote9anc|9]] Im Projekt verbleibende Stunden [[#sdfootnote10anc|10]] Vergangene Stunden am heutigen Tag [[#sdfootnote11anc|11]] Insgesamt vergangene Stunden [[#sdfootnote12anc|12]] Im Projekt verbleibende Stunden [[#sdfootnote13anc|13]] Vergangene Stunden am heutigen Tag [[#sdfootnote14anc|14]] Insgesamt vergangene Stunden [[#sdfootnote15anc|15]] Im Projekt verbleibende Stunden [[#sdfootnote16anc|16]] Vergangene Stunden am Heutigen Tag [[#sdfootnote17anc|17]] Insgesamt vergangene Stunden [[#sdfootnote18anc|18]] Im Projekt verbleibende Stunden [[#sdfootnote19anc|19]] Vergangene Stunden am heutigen Tag [[#sdfootnote20anc|20]] Insgesamt vergangene Stunden [[#sdfootnote21anc|21]] Im Projekt verbleibende Stunden [[#sdfootnote22anc|22]] Vergangene Stunden am heutigen Tag [[#sdfootnote23anc|23]] Insgesamt vergangene Stunden [[#sdfootnote24anc|24]] Im Projekt verbleibende Stunden [[#sdfootnote25anc|25]] Vergangene Stunden am heutigen Tag [[#sdfootnote26anc|26]] Insgesamt vergangene Stunden [[#sdfootnote27anc|27]] Im Projekt verbleibende Stunden Basil Lade Rafisa GmbH Bernstrasse 88 CH-8953 Dietikon