An der VHS Braunschweig findet ab dem 23.04.2018 ein Bildungsurlaub zum Thema "Dynamische serverseitige Webseiten mit PHP & MySQL" aus der Zertifikatsreihe "CMS Online Designer (VHS)" statt. Anhand praktischer Beispiele wollen wir uns die aktuellen Techniken rund um PHP-Skripte und MySQL-Datenbanken erarbeiten.
Ort: Heydenstraße 2, VHS Braunschweig, Raum 2.11
Zeiten: Mo, 23.04. bis Fr, 27.04.18; jeweils von 08.30 - 16.00 Uhr
Freiwillige Prüfung: eine freiwillige Prüfung wird mit den Interessierten im Seminar koordiniert
Beachten: Online Anmeldung über VHS BS Website oder Frau Jackmann (Prüfungsnummer MWP04)
Termin Prüfung: Di. 15.05.2018; 16.30 Uhr (Raum 2.11)
Wir wünschen - wie immer - viel Spaß und Erfolg bei unseren Seminaren.
Ihr Trainer Joe Brandes - hier folgt unser "Roter Faden" für die Woche ...
Tag 01
Montag, 23.04.2018, 08.30 - 16.00 Uhr
- Orientierungsphase, Teilnehmer-Themen, Pausenregelung / Seminarablauf
Cobra-Shop: gesponserte Software für IT-Teilnehmer bei VHS-EDV-Seminaren;
Buchempfehlungen: meine "persönliche aktuelle Bibliothek" werde ich im Seminar vorlegen;
die digitalen Daten der Woche stehen den TN am Freitag im Netz zur Verfügung - Software- und Grundeinrichtung PCs
Reborn-Card bzw. Software-Absicherungen der Seminar-PCs beachten:
wir arbeiten in den BU-Profilen ohne Rücksetzung von Laufwerk C: (Bezeichner: Windows 10 BU)
Programme / Software: (sind auch in meinem aktuellem XAMPP-CMSOD in portabler Version eingebunden)
Packer: 7-Zip als Tool zum Packen/Entpacken
Browser: Mozilla Firefox, Chrome, Opera, Safari (IE-Alternativen und Developer-Tools)
Editor: Notepad++, PsPad (multifunktionaler Editor mit Erweiterungsmöglichkeiten / PlugIns)
Software nutzen:
Variante A PortableApps aus XAMPP-CMSOD nutzen
Anm.: bitte nicht aktualisieren!Variante B lokal installierte Windows-Programme nutzen
Anm.: bei Notepad++ bitte die 32-Bit-Version nutzen,
damit die optimale PlugIn-Unterstützung gewährleistet ist!
Explorer konfigurieren (Win + E): Dateiendungen nicht mehr Ausblenden lassen (bzw.: versteckte und Systemdateienen einblenden)
Windows Vista / 7 / 8 / 10: Organisieren - Ordner- und Suchoptionen - Register Ansicht ...
Alternativ bei Windows XP: Menü Extras - Ordneroptionen
Grund: Dateien heißenbild.jpg
oderbild.jpeg
oderbild.JPG
- wir müssen das genau wissen! - Client-Server-Prinzip für WWW
WWW - World Wide Web; Internet "Dienstleistung / Service / engl. Daemon - siehe angehängtes "d" bei httpd für Apache-Server-Software"
Web-Client: genannt Browser, die über ein Protokoll (http, https) vom Server Daten abfragen (request)
Browser-Software: Microsoft Internet Explorer, Google Chrome, Mozilla Firefox, Apple Safari, Opera, ...
Web-Server: "Dienstleister"-Software - Apache ("A Patchy Server" - historisches Wortspiel für alten NCSA Webserver Mosaic, der durch "Flicken" verbessert und dann eigenständig entwickelt wurde)
Alternative Web-Server-Software: IIS (Internet Information Server von Microsoft); nginx von Firma NGINX, LIGHTTPD, ab PHP 5.4 ist in der Skriptsprache PHP auch ein "Server" eingebaut (Link)!
Zusammenspiel: "Kopiermaschine" - Client fragt nach Webdokument - Server bereitet Daten auf und kopiert an Client
Wichtig: unsere serverseitigen PHP-Dokumente müssen immer über eine http-Url im Browser aufgerufen werden und die Dokumente sollten immer über die Dateiendung php verfügen - XAMPP-CMSOD installieren
Vorgehensweise im "Windows"-Seminar (XAMPP-Server für Windows):
Ich stelle als Trainer ein fertiges selbstentpackendes XAMPP-Archiv bereit, das bereits für die CMSOD-Seminare vorkonfiguriert ist und auch Beispielinstallationen enthält!
Das Archiv wird wieder mit der Notwendigkeit zu PHP Version 7.2 aktualisiert (siehe TYPO3 v9 - Version 9)!
Archivname: xampp-cmsod-TEILNEHMER-20171015-1630.exe MD5-Prüfsumme: b939515595f0e4d6685996a0386f2ecd
2) Selbstentpackendes 7z-Archiv mit Doppelklick "öffnen/entpacken"
3) Zielordner:C:\xampp-cmsod
angebeben (Anm.: Entpacken in ZielpfadC:\xampp-cmsod
ist wichtig!)
Grund: alle eingebauten CMSOD-Techniken und Tools auf dieses lokale Verzeichnis optimiert wurden.
Tipp: eigene XAMPPs kann man dann einfach in anderen Verzeichnissen wieC:\xampp
unterbringen.
4) Starten / Doppelklickenxampp-control.exe
(Kontrollcenter) und
5) Starten der nötigen Server mit Hilfe des Kontrollcenters:
→ Apache Webserver und
→ MySQL Datenbankserver per Schaltflächen "Starten" im Kontrollcenter
Anm.: die Nachfragen der Windows-Firewall bei den Dienste-Starts können abgebrochen werden, da wir keine Zugriffe auf unsere lokalen Testinstallationen über das Netzwerk wünschen. Die Aktionen zum Starten Webservice Apache und Datenbankserver MySQL (bzw. MariaDB) sollten wir täglich (also jeweils bei Bedarf) durchführen.
Empfehlung: von einer automatischen Bereitstellung des XAMPP-Server über (automatisch startende) Services (Dienste) rate ich ab!
6) Browser / Webclient - Starten des für Webentwicklung CMSOD optimierten Firefox über Schaltfläche "Admin" im Kontrollcenter bei Webserver Apache
Hinweis: dieser portable Firefox hat bereits Standard-AddOns installiert und nutzt z.B. auch keinen Browser-Cache; Sie können einen installierten und diesen mobilen Firefox nicht gleichzeitig nutzen!die vorbereitete Umgebung:
Technik Info Bemerkung XAMPP C:\xampp-cmsod\
Basispfad des XAMPP-CMSOD Tools / Software C:\xampp-cmsod\_tools
Software, PortableApps Web-Dokumente C:\xampp-cmsod\htdocs\typo3\...
mit Unterordnerstrukturen Apache C:\xampp-cmsod\apache\conf\httpd.conf
Konfigurationsdatei Webserver PHP C:\xampp-cmsod\php\php.ini
Konfigurationsdatei PHP Für Technik- und CMSOD-Zertifikat-Interessierte befindet sich unter
(URL) http: //localhost/cmsod-overview
eine Website mit Infos rund um den CMSOD
und die genutzten Techniken und Konfigurationen.
- Serverumgebung XAMPP - Seminar-Version: XAMPP 7.1.8 mit PHP 7.1.8
X - Betriebssysteme Linux (L), Windows (W), MacOS (M) oder Sun Solaris;
A - Apache (Webserver),
M - MariaDB / MySQL (Datenbankserver),
P - PHP (serverseitige Skriptsprache)
P - Projekt;
bei Hostern:
klassisches LAMP-System (Linux - Apache - MySQL/MariaDB - PHP)
oder auch mal als WAMPP mit einer Windows Server Betriebssystem Basis und dem IIS (Internet Information Server) als Webserver und dem MSSQL-Server als Datenbankserver
Hier eine kleine Übersicht/Zusammenfassung zur genutzten AMP-Umgebung:
AMP A - Apache M - MySQL / MariaDB P - PHP Technik Web-Server Datenbank-Server serverseitige Skriptsprache Web (URL) httpd.apache.org
(Projekt Apache Foundation)www.mysql.com
(bzw. .de)
mariadb.orgwww.php.net
(bzw. de.php.net)Konfigurationen httpd.conf my.ini
(bzw. my.cnf)php.ini XAMPP Versionen 2.4.27 10.1.26-MariaDB
mit PhpMyAdmin 4.7.07.1.8 - Web-Dokumente-Ordner
Erkenntnis: XAMPP-Verzeichnis: c:\xampp-cmsod\htdocs (entspricht der Request-URL: http://localhost/)
Übungsordner C:\xampp-cmsod\htdocs\phpmysql\phpmysql-seminar
URL http://localhost/phpmysql/phpmysql-seminar
Wichtige Empfehlungen:
keine Umlaute, keine Sonderzeichen, Groß- und Kleinschreibung beachten (also Empfehlung: alles klein schreiben), keine Leerzeichen!
Bei Anfrage von Verzeichnissen (ohne Webdokument) gibt XAMPP ein Inhaltsverzeichnis (Index) zurück!
Standarddokumente sind mit Reihenfolge in Konfigurationsdateihttpd.conf
des Webservers hinterlegt (z.B. index.php index.html index.htm index.php3 ...)
Das ist auf Produktionssystemen (also beim Live-Hosting) nicht gewünscht und entsprechend dort auch anders konfiguriert. - Notepad++ Tipps - Standardeditor für das Seminar
Anm.: in XAMPP-CMSOD PortableApps eingebaut - bitte 32-Bit-Variante nutzen wegen PlugIns
Hilfe PHP: Cursor in PHP-Technikwort und Online-Hilfe mit Alt + F1 aufrufen
Code-Completion (Codevervollständigung) mit Strg + Leertaste
Zoomfaktor/Schriftanzeigengröße mittels Strg + Rollrad-Maus oder Strg + "+" (oder -) auf Nummernblock
Für sauberes Syntax-Highlighting und Codecompletion benötigt Notepad++ Datei mit gespeichertem Dateiformat (Menü Sprachen)
Tipp: Über Kontextmenü auf PHP-Datei den portablen Editor Notepad++ als Standardprogramm zum Öffnen einstellen
Pfad: C:\xampp-cmsod\_tools\_portable\PortableApps\Notepad++Portable\Notepad++Portable.exe
oder natürlich den in Windows installierten Notepad++-Programme-Pfad! - Zeichencodierungen
die IT-Systeme kennen nur 0 und 1, also verwendet man "Tabellen", die 0/1-Kombinationen dann Zeichen zuordnen
Beispiele: ASCII, ANSI, ISO-8859, UTF-8 (und andere UTF-Varianten; auch als Unicode bezeichnet);
nur in 7 Bit also den ersten 128 Zeichen sind diese "Nachschlagetabellen" gleich
Empfehlung: bitte in UTF-8 bzw. UTF-8 ohne BOM speichern mit Notepad++ ;
gerne zusammen mit sauberem HTML-Header (<meta charset="utf-8">
)
Hinweis zu CMS Joomla: dort sollten alle Konfigurationsdateien (*.ini) unbedingt mit UTF-8 ohne BOM gespeichert werden - PHP-Hilfe nutzen
Aufruf der Online-Hilfe von PHP-Webportal php.net mittels Cursor in PHP-Wort und Alt + F1
Übung: Bereitstellung der PHP-Hilfe als CHM-Datei - wichtig: CHM-Dateien müssen seit Windows XP SP2 erst in Kontextmenü "zugelassen" werden
Link zu (deutschen) PHP-Dokumentationen - Mischung von HTML und PHP
letztendlich wollen wir natürlich komplette Webseiten an den Client ausliefern - also benötigen wir HTML-Gerüste und bringen dort unsere PHP-Skriptzeilen unter; das werden wird aber nur erst mal ausprobieren und verstehen und uns dann wieder auf PHP konzentrieren
Anm.: in vielen Beispielen werden zur schnellen Verdeutlichung direkte Styles in HTML verdrahtet - also ohne saubere CSS Stylings; das muss natürlich später sauber gemäß der Techniken aus "HTML & CSS" codiert werden!
Für das Einfügen von PHP beschreibt das PHP-Handbuch unterschiedliche, technische Möglichkeiten. Auszug aus der PHP-Hilfe: "...Es gibt vier unterschiedliche Paare öffnender und schließender Tags, die in PHP verwendet werden können.
Empfehlung:<?php ?>
Die Einbindung mit<script>
-Tag ist mit PHP7 nicht mehr gewünscht. Die anderen Varianten sind Short-Tags und ASP-Tags, die über das php.ini-Konfigurationsfile ein- und ausgeschaltet werden können. ..." (Link)
Tipp für komplettes HTML5-Gerüst:html:5
(dannStrg + ,
← Tastenkombination mit Strg und Komma; Erkl.: Erweiterung Emmet in Notepad++) - Kommentare in PHP
Einzeilig mit //, mehrzeilig mit /* ... */,
spezielle Kommentartechniken mit automatischen Dokumentationserstellungen (siehe phpDoc)
Nutzen zum Auskommentieren von Zeilen - also Testzwecken - oder natürlich der Erläuterung von Code im Skript (wichtig für spätere Nachvollziehbarkeit und Analyse / Anpassung) - PHP-Status (Version) und Konfigurationen ermitteln -
phpinfo()
- unser erstes Skript (Link php.net)
Ein kleines Skript erledigt die Arbeit{code lang:php showtitle:false lines:true hidden:false}<?php
// Kommentar: Wir wollen die PHP-Server-Konfiguration auslesen!
phpinfo();
?>{/code}Achten Sie bei Ausdrücken/Anweisungen in Skriptsprachen generell auf das hier auch abschließende "; (Semikolon) echo
(Sprachkonstrukt zur Übergabe von Strings an Browser - Link php.net)
Ausgabe von Text und Variablen (beginnen immer mit $), Konstanten werden mitdefine
vereinbart
bei Verwendung von doppelten Anführungszeichen (double quotes) werden integrierte Variablen in der Ausgabe (im String) ausgewertet
die einfachen Anführungszeichen (single quotes) erlauben die Verschachtelung mit doppelten Anführungszeichen (siehe HTML-Code class="top" oder ähnliche Syntax;
außerdem kann man Texte / Strings über den Operator "." aneinderheften oder über Kommas einfach nacheinander aufzählen
Alternative:print
- mit dieser Funktion kann man auch gleichzeitig Zuweisungen erstellen:
$var = print("Text"); // Zuweisung und Ausgabe - print arbeitet auch ohne Klammern- vordefinierte PHP-Konstanten ("magische" PHP-Konstanten - Link php.net)
__LINE__, __FILE__, PHP_VERSION, PHP_OS
Nutzung für "Debugging" und Analyse von PHP-Skripten date
(Datumsfunktionen - Link php.net)
Recherche mit PHP-Hilfe;
Übung mit Augabe (echo) des aktuellen Datums in der Form "2014-06-02"
$heute = date("Y-m-d"); // gibt das gewünschte Datum und Format aus
Funktion zum Erstellen von Datumswert:mktime
(Link php.net - bitte die Reihenfolge Monat, Tag beachten!)
Hinweis: DATETIME Formatierung mittels Formatierungsstring "Y-m-d H:i:s";
später für MySQL-Datenbank wichtig!- $_SERVER[...] (Link php.net)
technisch: Superglobales Array für die Nutzung von Server-Variablen
wie HTTP_USER_AGENT, SERVER_NAME und natürlich PHP_SELF
Aufruf mittels$_SERVER['PHP_SELF']
(wichtig für dynamische Website - Hauptseite ruft sich selbst immer wieder auf...) - Arrays (erste Annäherung - Funktion array Link php.net)
2-dimensionale Tabellen mit Spalte für Index und Spalte für Inhalt / Wert,
bei nummerischen Arrays laufen die Indizes von 0, 1, 2, ...
bei assoziativen Arrays (Hash-Tabellen) werden Begriffe / Bezeichner für die Indizes genutzt,
Definition von Array mit Hilfe von PHP-Aufruf array(...) und Zuweisung mittels "=>" - Arrayfunktionen -
array()
-foreach()
- Beispiele und Übungen
Beispiel (assoziatives Array):$arr = array( "Nr" => 12345, "Name" => "Meier", ...)
Beispiel (nummerisches Array):$arr2 = array( "Auto", "Bahn", "Fahrrad")
Definierten Wert mit Klammerschreibungen:$arr['Nr']
bzw.$arr2[2]
Funktioncount($arr)
ergibt die Anzahl von Werten im Array
Neuen Wert in nummerischem Array einfügen:$arr[] = "Flugzeug"
Ausgabe (zu Testzwecken) von Arrays mit Funktionenprint_r
undvar_dump
Tipp: Einschachtelung inpre
-Tags für formatierte Ausgabe im Browser (im Quellcode immer gut lesbar)
Automatische Schleifenfunktion für das Handling von Arrays:foreach
Arrays sind nur 2-dimensional (nur 2 Spalten) können aber auch noch verschachtelt werden! - Escape-Sequenzen (Link php.net)
Kennungen im PHP-Code für Spezialformate und Nutzung von sonst unerlaubten Zeichen
\n - Zeilenumbruch (im Quellcode)
\t - Tabulatorsprung (im Quellcode)
\" - Nutzung von doppelten Anführungszeichen; z.B. für Zuweisungnen wieclass=\"styling\"
- Verschachtelte Arrays
Arrays (hier in PHP ja nur 2-dimensional / 2-spaltig) lassen sich gerne verschachteln
Übung:
Länder Hauptstadt Währung Sprache Deutschland Berlin Euro deutsch Japan Tokio Yen japanisch England London Pfund englisch
1) foreach verschachteln (nur bei vielen "Dimensionen nötig)
2) Funktionlist
nutzen, um aus dem Detaill-Array die gewünschten Variablen mit Funktionlist
zuzuweisen{code lang:php showtitle:false lines:true hidden:false}foreach ($laender as $country => $countryinfos) {
// mit Befehl list die Variablen hauptstadt, waehrung und sprache zuweisen:
list ($hauptstadt, $waehrung, $sprache) = $countryinfos;
// einfache Ausgabe von Land, Hauptstadt, Währung:
// echo $country, " ", $hauptstadt, " ", $waehrung, "<br>\n";
echo "<tr class=\"row\">
<td class=\"col\">$country</td>
<td class=\"col\">$hauptstadt</td>
<td class=\"col\">$waehrung</td>
<td class=\"col\">$sprache</td>
</tr>";
}{/code}Anm.: Bei umfangreicheren Codes wurde den TN der Code "fertig" zur Verfügung / zum Testen bereitgestellt. - Wichtige Anmerkung des Trainers zu den "Stylings" in den PHP-Codes:
Es wird häufig einfaches (physikalisches) HTML-Styling direkt in den Tags verwendet, statt sauberer CSS-Klassen und Definitionen.
Das geschieht, um die PHP-Codes einfach und übersichtlich zu halten. Später bitte natürlich saubere und professionelle Trennung von Layout (HTML-Struktur) und Styling (CSS) wie auf csszengarden.com ;-)
Tag 02
Dienstag, 24.04.2018, 08.30 - 16.00 Uhr
- Rekapitulation (→ ausführlich zu Tag 01 und den wichtigen Array-Techniken), TN-Fragen
- Übung zu verschachtelten Arrays
mit Tabelle zum CMSOD:
Modul Hauptstadt Währung I HTML & CSS statische Webseiten II PHP & MysQL dynamische serverseitige Skripttechnik + Datenbank IIIa Joomla! Content Management System IIIb TYPO3 Content Management System - php.ini - Konfiguration für PHP
Erinnerung an Analyse der PHP/Apache-Umgebung mittelsphpinfo()
; Firma Zend erwähnt (PHP Sprache Entwickler)
hier kann auch der genaue Pfad zur genutzten (geladenen) php.ini gecheckt werden!
Konfigurationen angesprochen: z. B. post_max_size , memory_limit , ...
error_reporting - unterschiedliche Vorschläge für Entwicklungs- (Development) und Produktions-Server
display_errors - umgeschaltet von Off auf On , damit wir die Reaktionen auf "Tippfehler" in unseren Codes überhaupt deuten können
Wichtig: die neue php.ini muss neu in Webserver-Technik geladen werden - also: Apache restarten über XAMPP-Konfiguration - Kontrollstrukturen (Link php.net)
Nutzung von
Verzweigungen mittelsif
(bzw.if ... then ... else
) undswitch
,
Schleifen mittelsfor, foreach
undwhile
bzw.do...while
(Abbruchbedingungen am Anfang bzw. am Ende),
Verschachtelungen vonfor
-Schleifen
kleine Übungen zu if, switch und for (inklusive Verschachtelung ("kleines Einmaleins")
Anm: Bedingungen mit > (größer), < (kleiner), <> (ungleich), == (gleich), === (identisch, gleiche Typen), ... (Link Vergleichsoperatoren php.net)
Zusatzinfo: Logische Funktionengettype()
,empty()
,is_null()
,isset()
undboolean()
und Nutzung von Vergleichen mit = , == und === als tabellarische Übersicht (Link Ausdruck / Funktionen php.net)
Darstellung von Logik und Kontrollstrukturen in Blockdiagrammen nach Nassi-Shneiderman (Link Wikipedia);
Verneinungen mit vorgestelltem Ausrufezeichen (!) ;
Verknüpfung von mehreren Bedingungen:
mit && (beides muss gleichzeitig erfüllt sein - siehe Logische Operatoren Link php.net)
Hinweis zu Seminar: wir nutzen die Strukturen ohne ausführliche Syntax und Detailerläuterungen - hierzu bitte selber jeweils experimentieren und die Darstellungen der Seminarwoche nachvollziehen.
Tipp: Das PHP-Manual bietet ausreichende Beispiele zu den wichtigsten Kontrollstrukturen (hier gerne "copy & paste" nutzen) - String-Funktionen (Link php.net)
addslashes, stripslashes
- Anführungszeichen sequenzieren (sonst Datenbank-Probleme möglich)explode, implode
- Auseinanderbauen und zusammensetzen von Zeichenkettensubstring, str_replace
- Beispiele für Zeichenkettenmanipulierung (siehe auch str_replace für Grundprinzip dynamischer Websites in Folgeübungen)print, printf, sprintf
- Ausgabe formatierter, intelligenter Zeichenketten (Link sprintf php.net){code lang:php showtitle:false lines:true hidden:false}$anzahl = 5;
$ort = 'Baum';
$format = 'Es sind %d Affen auf dem %s';
sprintf($format, $anzahl, $ort);{/code}htmlspecialchars
- macht aus Tags - also spitzen Klammern die HTML-Sonderzeichen und "entschärft" somit Textketten mit solchem Code, siehe spätere Formular-Behandlung (Validierung) bei kompletten und umfangreichen PHP-Lösungennl2br
- gibt Texte inklusive Zeilenumbrüchen an Browserrawurlencode
- ersetzt in Strings für URLs die Leer- und Sonderzeichen (wichtig für GET)get_meta_tags
- liest META-Daten aus head von HTML-Dokumenten aus; das funktioniert auch mit kompletten URL zu Online-Resourcen - Dateihandling (Übung: lesen und schreiben von Textdateien)
File-Handle übernehmen mitfopen
(Link php.net) und
schließen mitfclose
(zurückgeben/schließen bitte nicht vergessen!),
Unterschiedliche Modi für den Dateizugriff (siehe fopen in Manual); weitere Funktionen:file
(und wieder erhalten wir ein Array!),file_exists
(prüft Existenz einer Datei - siehe auch Dynamische Website "Diashow")fgets
(lässt sich automatisch mit while schleifen),dir
(Nutzung einer Verzeichnisklasse in PHP - erster Hinweis auf OOP - Objektorientierte Programmierung mit Aufrufen wie$folder->read()
)
Anm.: Dateibehandlung und Ordnerberechtigungen auf WAMP-System abweichend von Hostern
Grund: Linux mit anderen Datei-/Ordnerberechtigungen und Benutzerkonzept - MySQL (Teil I - eine erste Annäherung an den Datenbankserver mysqld)
technisch: RDBMS - Relationales Datenbank Management System
Erläuterungen zu den Normalformen mit Hilfe von Auszug aus MySQL-Buch von M. Kofler
Plan: DB für Bücher macht aus einer ersten Rohtabelle eine Anordnung (also: in Relationen befindliche 4 Tabellen!)
Ziel: Vermeidung von Inkonsistenzen und Redundanzen
Historie MySQL: Entwickelt von Firma "MySQL AB" (heute MySQL bei Oracle),
Aktuelle Alternative zu MySQL von Ur-Entwickler Michael Widenius ist MariaDB (Namensgebungen nach Töchtern My und Maria)
Freie MySQL-Variante für Entwickler und Tester unter dem Namen "MySQL Community Edition"
Infoportale MySQL: International - Deutsches Portal - Dokumentation
Infoportal MariaDB: International - Learn MariaDB (im XAMPP ist seit geraumer Zeit MariaDB eingebaut!)
SQL Structured Query Language (viele Dialekte aber gemeinsame Basis),
Client-Server Prinzip; Client in Konsole (Eingabeaufforderung)mysql
gezeigt MySQL-Kommandozeilentools in cmd (Eingabeaufforderung) oder PowerShell
Client-Tools bei XAMPP in XAMPP-Ordnerc:\xampp-cmsod\mysql\bin
,
verbinden / konnektieren mittelsmysql -h localhost -u root -p
(beim XAMPP kann man localhost weglassen)
Beispielzeilen / Beispielaufrufe in der mysql-Konsole{code lang:sql showtitle:false lines:true hidden:false}SHOW DATABASES;
USE cdcol;
SHOW TABLES;
SHOW FIELDS FROM cds;
SELECT * FROM cds;
INSERT INTO cds (titel, interpret, jahr) VALUES ("Testalbum", "Joe Prince", "2016");{/code}Verwaltung mittels diverser "Clients" möglich:
1) php-Skripte mit Modul/Schnittstelle mysqli (das letzte "i" steht für improved / verbessert
2) phpMyAdmin - eine PHP-Skriptsammlung für dei Administration von MySQL-Datenbankservern
Übungen mit phpMyAdmin: Datenbank "dienstag" mit 2 Tabellen "autor" und "title" (also die Bücher)
erste Tests mit SQL-Befehlen über die Oberfläche von phpMyAdmin; speziell: Feldeigenschaften AutoIncrement (AI), Primary Key (PK)
3) Kommandozeile/Shell mit Tool mysql (siehe Ordner C:\xampp-cmsod\mysql\bin - Anm.: bin für Binaries, also ausführbare Programme)
4) Spezialprogramme zur DB-Verwaltung (z.B. MySQL Workbench - Link)
Nötige Zugangsdaten zur Konnektion (DB-Connection) zum Datenbankserver:
(hier: Infos für den Zugriff auf eine Datenbank bei unserem XAMPP-CMSOD)
Datenbankserver: localhost (beim Hoster z.B. db12345.1und1.com)
Benutzername: root
Passwort: LEER (kein Passwort)
Datenbank: (z.B.) cdcol (CD Collection - mit Tabelle cds )
Tag 03
Mittwoch, 25.04.2018, 08.30 - 16.00 Uhr
- Rekapitulation, Teilnehmer-Fragen, Prüfungsinteresse abklären (?)
- Bibliothek (Bücher)
Als Trainer habe ich diverse Bücher zu den Themen PHP & MySQL bereitgestellt (siehe Bibliothek auf diesem Info-Portal)
Hinweis auf Buchhandel von "Graff - Braunschweig - graff.de" bis hin zum Terrashop und Verlag "Rheinwerk-Verlag (früher: Galileo-Verlag) - Formulare
HTML-Tags:form
(Atrribute: action, method), Methoden: POST vs. GET,input
(Attribute: type, name)
POST-Methode: Übergaben im Hintergrund (unsichtbar), kein Protokollieren im Webserver, größere Datenmengen (beschränkt durch php-Konfiguration - siehe post_max_size)
GET-Methode: Daten lassen sich als URL speichern (Bookmark, Link)
Seit PHP 5.3 keine übergebenen Variablen "wie selbstverständlich" (also global!) über einfachen Variablennamen verfügbar:
es gibt keine sicherheitstechnisch fragliche PHP-Konfigurationregister_globals = On
mehr!
Übergabe der Variablen also immer mittels der Arrays$_GET['name']
bzw.$_POST['name']
(s. Handbuch Superglobals)
Speziell: Bereinigung von Eingaben mittels Funktionhtmlspecialchars
;
Formularthemen: siehe auch Captchas und Formular-Validierungen (haben hier keinen Platz - siehe dazu auch Seminar "HTML & CSS")
das aktualisierte selfhtml-Wiki hat gute Infos zu modernen Formularen (HTML5)
Wichtig: sich selbst aufrufende Formulare; Nutzung von PHP_SELF im superglobalen Array $_SERVER - also$_SERVER['PHP_SELF']
nutzen - Diashow (dynamische Website ohne DB-Anbindung)
beispielhaftes Skript nach M. Lubkowitz aus der Internet Pro
Anm.: Jahrgang 2003 - aber thematisch / technisch immer noch passend!
Grundidee: Skriptdiashow.php
bzw.index.php
liest Steuerdatei (bayern.csv
) und ein HTML-Template (template.htm
) ein und berechnet und ersetzt Platzhalter im Template mit der PHP-Funktionstr_replace
Übernahme der Steuerdaten für Diashow-Datei und Bildnummer mit GET-Technik:
./diashow.php?fn=bayern.csv&nr=3
genutzte Funktionen:file, implode, explode, list, str_replace
Speziell: Kurzform für if-then-else-Struktur$pic_nr = $_GET['nr'] ? $_GET['nr'] : 1;
Coeschnippsel aus Diashow; das Skript wurde ausgiebig analysiert und getestet{code lang:php showtitle:false lines:true hidden:false}// Template laden
$output_html = implode('', file('template.htm'));
// CSV-Datei einlesen - ergibt Array $pics
$pics = file($_GET['fn']);
// Nummer des anzuzeigenden Bildes festlegen (Kurzschreibweise if):
$pic_nr = $_GET['nr'] ? $_GET['nr'] : 1;
// hier die LANGE Schreibweise mit if-then-else:
if ($_GET['nr']) {
$pic_nr = $_GET['nr'];
} else {
$pic_nr = 1;
}
// Bilddatensatz laden - Dateiname / Beschreibung zuordnen aus $pics
list($filename, $description) = explode(';', $pics[$pic_nr-1]);{/code}Basierend auf diesem Grundprinzip für "Dynamische serverseitige Webseiten" wurde eine wirkliche Webseite für die TN bereitgestellt (Projektordner dyn-web). Dort dann Umsetzung mit automatischer Bereitstellung einer Standard-Startseiteindex.php
mit Vorgabe der Startparameter, falls in URL keine GET-Variablen übergeben werden!
URL dyn. Seiten - das Grundprinzip dynamischer Webseiten also:
(Anm.: Prinzip wurde auch beispielhaft mit Joomla-CMS gezeigt)./index.php?parameter1=xyz¶meter2=abc&dyninhalt=impressum
- phpMyAdmin - der Standard für die "manuelle" Nutzung von MySQL-Datenbankservern
bereits in XAMPP unterlocalhost/phpmyadmin
erreichbar (oder natürlich auch127.0.0.1/phpmyadmin
)
Konfigurationsdatei für MySQL Datenbankserver:my.ini
(bzw.my.conf
)
Hierarchie: (siehe Breadcrumb/Brotkrumen-Navigations-Leiste oben in phpMyAdmin)
Übung mit Datenbankserver (localhost)
→ Datenbank (books)
→ Tabellen (author, title, rel_author_title, publisher)
→ Felder (nach Entwurfsvorlage "Kofler" im Seminar mit TN → mit Datensätzen / Records / Inhalten)
Verbindung zu Datenbankserver - nötige Daten/Infos:
Hostname, Benutzername, Passwort → dann Datenbank wählen (SQL-Befehluse
)
Hinweis auf Anzeige von SQL-Kommandos in der Oberfläche für die ausgeführten Aktionen
so lässt sich viel Wissenswertes zur Abfragesprache SQL lernen und aufschnappen;
Änderung des Anzeigedesigns ist möglich; schnelle Navigation über Breadcrumb-Leiste oben
Übung: Erstellen / Datensätze / Exportieren / Importieren
einer Datenbank books nach Vorbild "MySQL - Kofler - Normalformen"
das MySQL-Client Tool zum Exportieren:mysqldump
(Imports mittelsmysql
)
Automatisierung auf Linux/Unix mittels Cron-Jobs (termingeplanten automatischen Ausführungen)
Analyse / Ansicht der SQL-Import-Datei mit geeignetem Editor (Notepad++ nutzen - keine Windows Editoren wie Notepad oder Wordpad)
Insbesondere Hinweis auf Struktur-Ansicht mit MySQL-Datentypen (int, bigint, varchar) und Eigenschaften (Auto Increment AI, Primärschlüssel / Primary Key PK) - PHP & MySQL Projekte (lokal und beim Hoster)
Hosting Beispiele und Preise (1und1, Strato, Hosteurope), Webspeicher (GB), Anzahl Datenbanken
Spez. TYPO3-Hoster und -Agenturen: JWeiland, Mittwald
Projekte bestehen aus zwei technischen Bestandteilen bestehen:
1) Webserver htdocs Verzeichnis(-struktur) - kann beim Hoster leicht per FTP genutzt werden
2) Datenbank und deren Tabellen (Struktur + Daten) - hier helfen jetzt MySQL-Dumps (Importe / Exporte): - MySQL Dumps (Backups für Datenbanken / Tabellen - Importieren / Exportieren)
mit PhpMyAdmin DBs /Tabellen per Exportieren und Importieren Dateien mit und ohne Kompression (sql, zip gzip, bz2),
Anm. zur Praxis beim Hosting: beachten Sie, dass die meisten Hoster nicht das Zurücksichern (importieren) ganzer Datenbanken erlauben, daher sollten Sie zumeist die Tabellen "dumpen" und nicht die jeweilige komplette Datenbanken!
Probleme / PHP-Techniken (siehephp.ini
- die PHP-Konfigurationsdatei):
Beschränkung der Dump-Größe (upload_max_size
,post_max_size
),
Laufzeit der PHP-Skripte (max_execution_time
) für das Komprimieren und Dumpen
Tipp: falls mal sehr große und hostingseitige Dumps problematisch sind (Menge der Daten bzw. Zeit):
Skriptsammlung von www.mysqldumper.de - CRUD - Create, Read, Update, Delete
die grundlegenden Datenbankoperationen (Wikipedia-Link)
CRUD Beschreibung SQL-Kommandos Create Erstellen neuer Datensätze
Erstellen neue Datenbank
Erstellen neue TabelleINSERT INTO ...
CREATE DATABASE ...
CREATE TABLE ...Read Lesen / Auslesen von Datensätzen SELECT ... FROM ... WHERE ... Update Aktualisieren von Datensätzen UPDATE ... Delete Löschen von Datensätzen
Löschen einer Datenbank
Löschen einer TabelleDELETE ... FROM ... WHERE ...
DROP DATABASE ...
DROP TABLE ...
Tag 04
Donnerstag, 26.04.2018, 08.30 - 16.00 Uhr
- Rekapitulation, Teilnehmer-Fragen
- phpMyAdmin - SQL Tests
Forts. DB-Technik... ausgiebige Tests beginnend mit einfachen SELECT-Strings{code lang:sql showtitle:false lines:true hidden:false}select * from publisher
select publisher from publisher
select * from title
select titleId, title from title
select * from title where (year = 2017)
select * from title where (year > 2014){/code}Abfragen an mehreren Tabellen:
Problem mit "Permutationen":
aus 4 Titeln (Tabelle title - Bücher) und 3 Verlagen (Tabelle Publisher) werden 3*4=12 Ergebnisdatensätze! {code lang:sql showtitle:false lines:true hidden:false}select title.titleId, title.title, title.publisherId,
title.year, publisher.publisherId, publisher.publisher
from title, publisher
select t.titleId, t.title, t.publisherId, t.year,
p.publisherId, p.publisher
from title t, publisher p{/code}Abfragen mit Filter (WHERE) für Datensätze mit gleicher publisherId:
Dadurch werden die passenden Bücher-Verlage Kombinationen aus den 2 Tabellen selektiert!{code lang:sql showtitle:false lines:true hidden:false}select t.titleId, t.title, t.year,
p.publisherId, p.publisher
from title t, publisher p
where (t.publisherId = p.publisherId)
select t.titleId, t.title, t.year,
p.publisherId, p.publisher
from title t, publisher p
where (t.publisherId = p.publisherId)
order by t.titleId asc
select t.titleId, t.title, t.year,
p.publisherId, p.publisher
from title t, publisher p
where (t.publisherId = p.publisherId)
order by t.titleId desc
select t.titleId, t.title, t.year,
p.publisherId, p.publisher
from title t, publisher p
where (t.publisherId = p.publisherId)
and (t.year = 2017)
order by t.titleId desc
select t.titleId, t.title, t.year,
p.publisherId, p.publisher
from title t, publisher p
where (t.publisherId = p.publisherId)
order by t.titleId desc
limit 3{/code}Abschließende Tests mit Bearbeiten (UPDATE) und Löschen (DELETE) von Datensätzen.
Bitte für die passenden SQL-Befehle meine CRUD-Übersicht nutzen. - Funktionen (bzw. auch technisch: Prozeduren)
mit Konstruktionfunction
lassen sich benutzerdefinierte Funktionen erstellen
hier zwei einfache "Schablonen" - wir haben die zugehörigen Übungen im Seminar durchgeführt{code lang:php showtitle:false lines:true hidden:false}function arrayAusgabe ($arr) {
// Ausgabe mit print_r
echo "<pre>";
print_r ($arr);
echo "</pre>";
};
function quadriereWert ($val) {
// Quadrat berechnen:
$quadrat = $val * $val;
return $quadrat;
};{/code}Zur Erinnerung: unsere Funktionen im Seminar: arrayAusgabe, quadriereWert - include - require (externe Dateien einbinden / inkludieren)
Wir lagern die Funktionen (Prozeduren) in einem Extra-Skript aus und nutzeninclude
(dt.: einfügen von externem Code; oder auchinclude_once
) bzw.require
(einfügen/benötigen oder auchrequire_once
) in den PHP-Skripten, wo wir die Funktionen benötigen; so braucht man den Code nur einmal zu pflegen und zu warten und kann ihn mehrfach verwenden!
Sichtbarkeit von Variablen: $quadrat ist nur in function quadriereWert verfügbar - nicht im Hauptskript! - Gästebuch - PHP / MySQLi Techniken
Praxisbeispiel aus "PHP 7 und MySQL - Das umfassende Handbuch" (Autoren Hauser, Wenz)
ausführliche Darstellung der Basisskripte; Skripte befinden sich bei den digitalen Unterlagen zum Buch
Ergebniszeile
SQL in PhpMyAdmin
Query mit 2 Tabellen
CREATE TABLE
mysqli - Prepared Statement
Ergebnisobjekt
Tag 05
Freitag, 27.04.2018, 08.30 - 16.00 Uhr
- Rekapitulation, Teilnehmer-Fragen, Prüfungsinteresse klären (inkl. Termin)
- Forts./Rekapitulation zu "Gästebuch Übung"
Eigenes Skripting umsetzen nach diesen Vorgaben für Datenbank books und Tabelle title
Konzentration auf Basisskripte zum Auslesen und Ändern von Datensätzen
In Tabelle title nur publisherID ohne Verknüpfung zur publisher-Tabelle - Prozedurale vs. Objektorientierte Technik (OOP)
hier eine kurze Gegenüberstellung:
Prozedural es werden die eingebauten (mysqli_connect, mysqli_query) oder benutzerdefinierten Funktionen (s.o. arrayAusgabe) einzeln programmiert und aufgerufen OOP wir nutzen bestehende oder selbst definierte Klassen und Objekte
modern, effizient, wiederverwertbarer Code - erspart Zeit
Verzeichnisklassedir
(siehe Beispiel-Skript beim Datei- / Ordnerhandling)
MySQLi - Zugriff mit (verbesserter) MySQLi / PHP-Technik:$dbobjekt = new mysqli ( ... )
;$dbobjekt->query();
Empfehlung: OOP ist moderner und effizienter - also unser Ziel für die Nutzung von MySQL-Datenbanken mittels PHP/MySQLi
OOP-Vorteile: Wiederverwendbarkeit, Zeitersparnis beim Coden, leichte Erweiterbarkeit
OOP-Nachteile: anfangs verwirrend (viele Begriffe und Techniken), mehr Aufwand als mal schnell eine Funktion zu schreiben - OOP-Tutorials:
1) php.net - Klassen und Objekte Online Hilfe (Link)
2) Peter Kropff - Infoportal über diverse Online-Techniken (Link - Beispieldateien downloadbar)
3) Ab-Heute-Programmieren - Infoportal HTML, CSS, JS
Serie zu "OOP in PHP":
Teil 1 - Grundlagen (Link)
Objekte werden mit Hilfe von Klassen definiert;
Klassen besitzen Eigenschaften (Attribute / Variablen) und Methoden (Funktionen)
Teil 2 - Klassen und Objekte (Link)
Klasse definieren mitclass
;
Sichtbarkeit Variablen mitpublic
,private
Methoden mit function ; aktuelles Objekt mit$this->
Operator;
neue Objekte ("Instanzen") erzeugt man mitnew
;
Eigenschaften/Methoden für Objekt dann wieder mit -> Operator nutzen
Teil 3 - Eigenschaften, Methoden und Sichtbarkeiten (Link)
Eigenschaften mit public oder private deklarieren (Sichtbarkeiten; Anm.: in alten PHP-Codes statt public der Begriff var )public
- Eigenschaften/Methoden überall verfügbar; Standard falls keine Sichtbarkeit definiert wirdprivate
- Zugriff nur in der eigenen Klasseprotected
- eine Mischung aus public/private: die Sichtbarkeit nur in eigener und den Kindklassen (siehe Vererbung)
Teil 5 - Hilfsfunktionen (Link)class_exists()
- prüft, ob eine entsprechende Klasse existiertmethod_exists()
- prüft ob Methode (Funktion) existiertproperty_exists()
- prüft, ob eine Eigenschaft (Variable) existiert
get_class() , get_parent_class() - bitte selbst recherchieren (Selbststudium/Vertiefung)
Teil 6 - Magische Methoden (Link)__construct()
- Konstruktor (früher: function mit gleichem Namen wie Klasse)
wird automatisch bei Objekterzeugung (new) ausgeführt__destruct()
- Destruktor; wird automatisch bei Objektlöschung (z.B. mit unset $objekt) ausgeführt
weitere magische Methoden: __get() , __set() (Getter/Setter-Methoden)
Teil 8 - Vererbung Grundlagen (Link)
bestehende Klasse für neue Klasse nutzen:class auto extends fahrzeug
;
das spart dann richtig Arbeit und bringt Schwung in die Programmierung - OOP-Beispielskripte (Objektorientierte Programmierungen am Beispiel von Grafiken / Texten)
ausführliche Übungen mit Beispielcode, der den Teilnehmern zur Verfügung gestellt und kommentiert wird
Einführung mit Beispiel basierend auf Grafik-Bibliothek (GD-Bibliothek):
"Grüne Wiese" (Objekt von Klasse field) mit "Torstangen" (rect) und Bällen (ball); PHP erstellt PNG-Grafik
Textbasiertes Skript mit Klassen "Einzeller" und "Mehrzeller" - OOP mit PHP / MySQLi - MySQL Datenbanken mit PHP
die objektorientierte Nutzung basiert auf Basisklassenmysqli
,mysqli_result
undmysqli_stmt
(Gesamtübersicht zu Technik MySQLi auf php.net)
beispielhafte Verwaltung von "News" nach Beispielen der Online-PHP-Hilfe und der Ausarbeitung durch Dr. Florence Maurice (Website - Amazon)
Konnektion mit Datenbank und Darstellung der OOP-Vorgehensweise gemäß PHP-Online-Tutorial:
Technik: Prepared Statements - wir nutzen also die Klassemysqli_stmt
Vorteil dieser "vorbereitenden" Kommunikation zwischen PHP und MySQL:
sicherere Verbindung und Abwicklung von Aktionen mit Datenbankserver,
Gültigkeit (Validierung) vor Übertragung an den Datenbankserver,
Abfragestruktur getrennt von den Parametern für die gewünschten Aktionen (SQL-Strings),
Geschwindigkeitszuwachs bei aufeinanderfolgenden gleichartigen Aktionen{code lang:php showtitle:false lines:true hidden:false}<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection - errno Error Code - error Error String*/
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit(); // Alternative zu die(..);
}$city = "Amersfoort";
/* create a prepared statement */
if ($stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?")) {
/* bind parameters for markers */
$stmt->bind_param("s", $city);
/* execute query */
$stmt->execute();
/* bind result variables */
$stmt->bind_result($district);
/* fetch value */
$stmt->fetch();
printf("%s is in district %s\n", $city, $district);
/* close statement */
$stmt->close();
}/* close connection */
$mysqli->close();
?>{/code}Vergleichen Sie die Code-Bausteine mit unserem Praxisbeispiel und erkennen Sie "die Vorlage / das Praxisbeispiel"
Zuordnungen der Lösungen zu den Grundoperationen:
Erstellen neuer Datensätze:neu.php
Auslesen der Datensätze:anzeigen.php
Aktualisieren von Datensätzen:bearbeiten.php
Löschen von Datensatz:loeschen.php
Konnektion zum Datenbankserver / zur Datenbank:db_daten_aktuell.php
SQL-Dumps (nach manuellem Erstellen der Datenbank news ):tabelle_aktuell_erstellen.sql
,tabelle_aktuell_daten.sql
Gesamtübersicht über die MySQLi Klassen auf der php.net-Seite (Link)
Eigener Beitrag auf diesem Infoportal zu PHP/MySQLi/Prepared Statement/OOP Technikumsetzung inkl. komplettem Beispielcode - Cookies und Sessions (Link Cookies - Link Sessions auf php.net Online-Doku)
Cookies: Server legen Text/Infoseiten auf Client-Seite ab; Einsatzbeispiel: Infos zu Warenkorb, Merkzettel & Co; ID einer Session hinterlegen, Verwaltung mittels Superglobal$_COOKIE
, Befehl (Link php.net):setcookie()
; jeder Browser legt eigene Cookie-Verwaltung an und löst diese Verwaltung unterschiedlich (Mozilla Firefox: eingebaute DB sqlite; Microsoft Internet Explorer: Ordner für klassische Textdateien); {code lang:php showtitle:false lines:true hidden:false}<?php
if (!isset($_COOKIE["cook_1"])) {
setcookie("cook_1", "Hallo", time()+10, "", "",0);
echo "Cookie wurde gesetzt";
// echo $_COOKIE["cook_1"]; // geht hier nicht!
}
elseif ($_COOKIE["cook_1"]) {
echo "vorhandenes Cookie wurde gelesen: <br>";
echo $_COOKIE["cook_1"],"<br>";
}
echo "<pre>";
// ein Server - hier localhost - kann nur seine
// eigenen Cookies lesen!
print_r($_COOKIE);
echo "</pre>";
?>{/code}Beim Firefox erhalten Sie aktuell über die Einstellungen - Register Datenschutz - Link Cookies eine Übersicht
Sessions: werden auf Server verwaltet (siehe Infos bei phpinfo - session; Anmerkungen zu session.name - PHPSESSID bitte nicht verwechseln mit einer Session-ID), XAMPP--Session-Speicher-Pfad (session.save_path) aufC:\xampp\tmp
, Verwaltung mittels Superglobal$_SESSION
, {code lang:php showtitle:false lines:true hidden:false}<?php // Registrierung von Session-Variablen
// implizite Initialisierung der Session
session_start();
$id = session_id();
$_SESSION['ArtNr']=100101;
$_SESSION['BestNr']="999-8762";
echo "implizite Initialisierung der Session: ";
echo $id;
echo "<br>Artikelnummer: ", $_SESSION['ArtNr'];
echo "<br>Bestellnummer: ", $_SESSION['BestNr'];
?>{/code}Befehle:session_start()
,session_destroy
,$id = session_id()
für Prüfung wichtig! - Musterprüfung "PHP & MySQL - Modul II - CMSOD" gezeigt (Prüfungsvorbereitung)
wir haben die optimale Vorbereitung zur Prüfung besprochen;
im Ordner__backups
befindet sich eine Kurzanleitung (PDF) für die Nutzung der bereitgestellen "Backup / Restore"-Skripte - Nach dem Seminar ist vor dem Seminar / der Prüfung
ein paar Vorschläge zur Kursnachbereitung / Prüfungsvorbereitung:
- Lehrgangsinhalte (diesen Beitrag) nachvollziehen
- Übungen wiederholen und nach eigenen Ideen variieren
- Manuelles Erstellen der MySQL-Datenbank books nach Anleitung "Kofler / Seminar"
- OOP-Beispiele "Prepared Statements" wiederholen
Anm: eigener Beitrag zum Thema OOP auf diesem Portal
- Musterprüfung durcharbeiten
Hinweis: Musterlösungen nur Vorschlag! Auch abweichende Lösungen sehr wohl denkbar! - TN-Unterlagen (umfangreiche digitale "Downloads" über das Schulungsnetz)
- TN-Bescheinigungen, Feedback, letzte TN-Fragen
Vielen Dank für Ihre Super-Feedbackbögen und persönliche Anmerkungen und Interesse an weiteren Seminaren.
Ihr Trainer Joe Brandes