LaravelTutorial: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Celine (Diskussion | Beiträge) |
|||
(25 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 14: | Zeile 14: | ||
= Voraussetzungen = | = Voraussetzungen = | ||
* Visual Studio Code ist installiert, ebenso die Erweiterungen PHP, PHP Namespace Resolver, Laravel Blade Snippets | * Visual Studio Code ist installiert, ebenso die Erweiterungen | ||
* Composer ist installiert. | ** PHP, | ||
** PHP Namespace Resolver, | |||
** Laravel Blade Snippets | |||
* Composer ist installiert. <code>apt update && apt install composer</code> | |||
= Erstellen des Projekts = | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
USER=ck | |||
PROJ=notes | PROJ=notes | ||
BASE=/home/ws/php/$PROJ | BASE=/home/ws/php/$PROJ | ||
mkdir -p $BASE | sudo mkdir -p $BASE | ||
sudo chown USER $BASE | |||
cd $BASE | cd $BASE | ||
composer create-project laravel/laravel . | composer create-project laravel/laravel . | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Damit wird ein Verzeichnis $BASE/notes erstellt, und mittels Composer das Grundgerüst der Applikation erstellt. | * Damit wird ein Verzeichnis $BASE/notes erstellt, und mittels Composer das Grundgerüst der Applikation erstellt. | ||
* Es steht dann das PHP-Script | * Es steht dann das PHP-Script '''artisan''' zur Verfügung, mit dem Laravel-Kommandos gegeben werden können. | ||
* Interessant ist auch die Datei .env, in der die Konfiguration der Applikation stattfindet. | * Interessant ist auch die Datei .env, in der die Konfiguration der Applikation stattfindet. | ||
= Starten des Webservers = | |||
Es gibt einen eingebauten Webserver für die Entwicklung: | Es gibt einen eingebauten Webserver für die Entwicklung: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
Zeile 36: | Zeile 41: | ||
./artisan serve | ./artisan serve | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Die Applikation steht dann im Browser unter http://localhost:8000 zur Verfügung. | Die Applikation steht dann im Browser unter http://localhost:8000 zur Verfügung. | ||
= Modifizieren der Homepage = | |||
Wir öffnen den Editor "Visual Studio Code" in dem Projektverzeichnis: | |||
<syntaxhighlight lang="bash"> | |||
cd $BASE | |||
code . | |||
</syntaxhighlight> | |||
* Die erstellte Homepage steht in der Datei resources/views/welcome.blade.php | * Die erstellte Homepage steht in der Datei resources/views/welcome.blade.php | ||
* Diese Datei öffnen und im Quelltext was ändern | * Diese Datei öffnen und im Quelltext was ändern | ||
* Im Browser F5 drücken, der geänderte Text wird sichtbar | * Im Browser F5 drücken, der geänderte Text wird sichtbar | ||
= Neue Homepage erstellen = | |||
* Neue Datei: resources/views/home.blade.php Wichtig: endet immer mit .blade.php | * Neue Datei: resources/views/home.blade.php Wichtig: endet immer mit .blade.php | ||
* Eintippen: ! Es erscheint ein Icon "Schraubenschlüssel" mit Titel !. Dieses anklicken. Damit wird ein HTML-Grundgerüst erstellt. | ** In VSCode ganz links oben "Explorersymbol" (2 Dokumente) anklicken, so dass die Dateien sichtbar sind | ||
** Verzeichnis resources durch anklicken aufklappen, dort Verzeichnis "views" anklicken. | |||
** Oben gibt es ein Dokumentsymbol mit "+": Dieses anklicken, Namen "home.blade.php" eintragen, RETURN. | |||
* In der leeren Datei: | |||
* Eintippen: ! Es erscheint ein Icon "Schraubenschlüssel" mit Titel "!". Dieses anklicken. Damit wird ein HTML-Grundgerüst erstellt. | |||
* Titel korigieren: <title>Notes</title> | * Titel korigieren: <title>Notes</title> | ||
* Im Body: | * Im Body: | ||
< | <syntaxhighlight lang="html"> | ||
<h1>Test</h1> | <h1>Test</h1> | ||
</ | </syntaxhighlight> | ||
* Speichern | * Speichern | ||
* Datei routes/web.php | * Datei routes/web.php | ||
< | |||
<syntaxhighlight lang="php"> | |||
Route::get('/', function () { | Route::get('/', function () { | ||
return view('welcome'); | return view('welcome'); | ||
}); | }); | ||
</ | </syntaxhighlight> | ||
* Hier steht der Aufruf der Datei welcome.blade.php | |||
* Wir ersetzen das durch unsere eigene Datei home.blade.php: | * Hier steht der Aufruf der Datei "welcome", was für "welcome.blade.php" steht. | ||
< | * Wir ersetzen das durch unsere eigene Datei "home.blade.php" mittels Eintrag von "home": | ||
<syntaxhighlight lang="php"> | |||
Route::get('/', function () { | Route::get('/', function () { | ||
return view('home'); | return view('home'); | ||
}); | }); | ||
</ | </syntaxhighlight> | ||
* Im Browser aktualisieren (F5): und wir sehen die neue Homepage. | |||
= Das Registrier-Formular = | |||
* resources/views/home.blade.php: Füge in den body-Block ein. | |||
= | <syntaxhighlight lang="html"> | ||
<div style="border: 3px solid black;"> | <div style="border: 3px solid black;"> | ||
<h2>Register</h2> | <h2>Register</h2> | ||
Zeile 79: | Zeile 102: | ||
</form> | </form> | ||
</div> | </div> | ||
</ | </syntaxhighlight> | ||
* @csrf: Sicherheit: https://en.wikipedia.org/wiki/Cross-site_request_forgery | * @csrf: Sicherheit: https://en.wikipedia.org/wiki/Cross-site_request_forgery | ||
* /routes/web.php: | * /routes/web.php: | ||
< | <syntaxhighlight lang="php"> | ||
Route::post('/register', function() { return 'We have registered'; }); | Route::post('/register', function() { return 'We have registered'; }); | ||
</ | </syntaxhighlight> | ||
* Browser aktualisieren (F5) | * Browser aktualisieren (F5) | ||
* Auf Button "Register" klicken | |||
= UserController erstellen = | |||
* Die Behandlung des Formulars passiert in einem "Controller". | |||
* Diesen können wir erzeugen, indem wir im Terminal (im Editor gibt es unten ein Registerblatt "TERMINAL"): | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan make:controller UserController | php artisan make:controller UserController | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Es entsteht die Datei app/Http/Controllers/UserController | * Es entsteht die Datei app/Http/Controllers/UserController | ||
* Wir erweitern die Klasse UserController um eine Methode register(), die die Registrierung vornimmt: | * Wir erweitern die Klasse UserController um eine Methode register(), die die Registrierung vornimmt: | ||
< | |||
<syntaxhighlight lang="php"> | |||
class UserController extends Controller | class UserController extends Controller | ||
{ | { | ||
Zeile 107: | Zeile 137: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
* Die Methode bekommt den Parameter $request, der die HTML-Infos der Seite enthält. | * Die Methode bekommt den Parameter $request, der die HTML-Infos der Seite enthält. | ||
* Der Aufruf von $request->validate() stellt Überprüfungen an: | * Der Aufruf von $request->validate() stellt Überprüfungen an: | ||
** In dem assoziativen Feld werden die zu überprüfenden Felder mit den Validierungsregeln verknüpft: | ** In dem assoziativen Feld werden die zu überprüfenden Felder mit den Validierungsregeln verknüpft: | ||
** Das Feld "name" darf nicht leer sein ("required"), der Name muss mindestens 3 und darf maximal 10 Zeichen lang sein, der Name darf noch nicht in der DB in der Tabelle 'users' im Feld 'name' vorkommen. | ** Das Feld "name" darf nicht leer sein ("required"), der Name muss mindestens 3 und darf maximal 10 Zeichen lang sein, der Name darf noch nicht in der DB in der Tabelle 'users' im Feld 'name' vorkommen. | ||
* Im Editor ist die Klasse '''Rule''' rot unterkringelt: Das symbolisiert einen Fehler: | |||
* Damit PHP die Klasse Rules in der Datei UserController.php kennt: Doppelklick auf Rule (Auswählen), dann Alt-Ctr-I (Import Class). In der Liste Illuminate\validation\Rule auswählen. | * Damit PHP die Klasse Rules in der Datei UserController.php kennt: Doppelklick auf Rule (Auswählen), dann Alt-Ctr-I (Import Class). In der Liste Illuminate\validation\Rule auswählen. | ||
* In der Datei routes/web.php: | * In der Datei routes/web.php: | ||
Wir ersetzen die <code>Route::post(...)</code> Zeile mit: | |||
<pre> | <pre> | ||
Route::post('/register', [UserController::class, 'register'] | Route::post('/register', [UserController::class, 'register']); | ||
</pre> | </pre> | ||
* Mit der URL /register wird die Methode 'register' in der Klasse UserController aufgerufen. | * Mit der URL /register wird die Methode 'register' in der Klasse UserController aufgerufen. | ||
Zeile 124: | Zeile 157: | ||
** Alle 3 Felder ausfüllen und dann Button "Register" klicken: Fehlermeldung, dass Benutzer nicht angemeldet ist. Das ist so korrekt, die Validierung ist demnach korrekt durchlaufen. | ** Alle 3 Felder ausfüllen und dann Button "Register" klicken: Fehlermeldung, dass Benutzer nicht angemeldet ist. Das ist so korrekt, die Validierung ist demnach korrekt durchlaufen. | ||
= Datenbank = | |||
== Datenbank anlegen == | == Datenbank anlegen == | ||
* Mit mysql die Datenbank dbnotes mit Benutzer notes und Passwort 'TopSecret' anlegen, z.B. mit | * Mit mysql die Datenbank dbnotes mit Benutzer notes und Passwort 'TopSecret' anlegen, z.B. mit | ||
< | <syntaxhighlight lang="bash"> | ||
sudo mysql mysql <<EOS | sudo mysql mysql <<EOS | ||
CREATE DATABASE dbnotes; | CREATE DATABASE dbnotes; | ||
GRANT ALL ON dbnotes.* to 'notes'@'localhost' IDENTIFIED BY 'TopSecret'; | GRANT ALL ON dbnotes.* to 'notes'@'localhost' IDENTIFIED BY 'TopSecret'; | ||
EOS | EOS | ||
</ | </syntaxhighlight> | ||
* In der Datei .env: | * In der Datei .env: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
Zeile 143: | Zeile 178: | ||
== Datenbank initialisieren == | == Datenbank initialisieren == | ||
* Laravel hat schon einige DB-Elemente vorbereitet. Diese werden so aktiviert: | * Laravel hat schon einige DB-Elemente vorbereitet. Diese werden so aktiviert: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan migrate | php artisan migrate | ||
Zeile 151: | Zeile 188: | ||
EOS | EOS | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Es werden die Tabellen failed_jobs migrations password_reset_tokens personal_access_tokens und users angelegt. | * Es werden die Tabellen failed_jobs migrations password_reset_tokens personal_access_tokens und users angelegt. | ||
* Die Tabelle users ist leer. | * Die Tabelle users ist leer. | ||
= Registrierung fertigstellen = | |||
* Änderung in app/Http/UserController.php: | * Änderung in app/Http/UserController.php: | ||
< | <syntaxhighlight lang="php"> | ||
public function register(Request $request) { | public function register(Request $request) { | ||
$incomingFields = $request->validate([ | $incomingFields = $request->validate([ | ||
Zeile 168: | Zeile 206: | ||
return redirect('/'); | return redirect('/'); | ||
} | } | ||
</ | </syntaxhighlight> | ||
* "User::" doppelklicken, Alt-Ctrl-i importiert die Klasse App\Models\User | * "User::" doppelklicken, Alt-Ctrl-i importiert die Klasse App\Models\User | ||
* Browser: F5, Felder ausfüllen, Button "Register" | * Browser: F5, Felder ausfüllen, Button "Register" | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
mysql -u notes -pTopSecret dbnotes <<EOS | mysql -u notes -pTopSecret dbnotes <<EOS | ||
Zeile 176: | Zeile 216: | ||
EOS | EOS | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Jetzt ist ein Datensatz in der Datenbank, der passende Benutzer | * Jetzt ist ein Datensatz in der Datenbank, der passende Benutzer | ||
* Fülle das Formular nochmal mit anderen Daten aus, damit 2 Benutzer existieren. | * Fülle das Formular nochmal mit anderen Daten aus, damit 2 Benutzer existieren. | ||
= Logout und Login = | |||
* In resources/home.blade.php: | * In resources/home.blade.php: | ||
< | |||
<syntaxhighlight lang="html"> | |||
<body> | <body> | ||
@auth | @auth | ||
Zeile 211: | Zeile 254: | ||
@endauth | @endauth | ||
</body> | </body> | ||
</ | </syntaxhighlight> | ||
* Änderung in app/Http/UserController.php: | * Änderung in app/Http/UserController.php: | ||
< | |||
<syntaxhighlight lang="php"> | |||
public function logout() | public function logout() | ||
{ | { | ||
Zeile 225: | Zeile 270: | ||
'loginpassword' => 'required' | 'loginpassword' => 'required' | ||
]); | ]); | ||
if ( | if ( | ||
auth()->attempt([ | auth()->attempt([ | ||
Zeile 236: | Zeile 280: | ||
return redirect('/'); | return redirect('/'); | ||
} | } | ||
</ | </syntaxhighlight> | ||
* Änderung in routes/web.php: | * Änderung in routes/web.php: | ||
< | |||
<syntaxhighlight lang="php"> | |||
Route::post('/register', [UserController::class, 'register']); | Route::post('/register', [UserController::class, 'register']); | ||
Route::post('/login', [UserController::class, 'login']); | Route::post('/login', [UserController::class, 'login']); | ||
Route::post('/logout', [UserController::class, 'logout']); | Route::post('/logout', [UserController::class, 'logout']); | ||
</ | </syntaxhighlight> | ||
* im Browser: F5 Felder für Login ausfüllen, Button "Login" -> "Your are logged in" | * im Browser: F5 Felder für Login ausfüllen, Button "Login" -> "Your are logged in" | ||
== Tabelle für Notizen erstellen == | == Tabelle für Notizen erstellen == | ||
Wir brauchen einen nichtflüchtigen Speicher für die Notizen, also eine Datenbanktabelle. Der Name sei notes. (Konvention: Kleinschreibung, Mehrzahl) | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan make:migration create_notes_table | php artisan make:migration create_notes_table | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Der Teil zwischen create_ und _table ist der Name der Tabelle, also "notes". | * Der Teil zwischen create_ und _table ist der Name der Tabelle, also "notes". | ||
* Es wird eine Datei database/migrations/xxx_create_notes_table.php erzeugt: Diese edieren. | * Es wird eine Datei database/migrations/xxx_create_notes_table.php erzeugt: Diese edieren. | ||
< | |||
<syntaxhighlight lang="php"> | |||
public function up(): void | public function up(): void | ||
{ | { | ||
Zeile 262: | Zeile 313: | ||
}); | }); | ||
} | } | ||
</ | </syntaxhighlight> | ||
* Es werden folgende Felder angelegt: | * Es werden folgende Felder angelegt: | ||
** 'id': der Primärschlüssel, erzeugt durch $table->id() | ** 'id': der Primärschlüssel, erzeugt durch $table->id() | ||
Zeile 269: | Zeile 321: | ||
** 'body': ein Feld vom Type LONGTEXT | ** 'body': ein Feld vom Type LONGTEXT | ||
** 'user_id': Ein Fremdschlüssel auf die Tabelle 'users' Feld 'id' | ** 'user_id': Ein Fremdschlüssel auf die Tabelle 'users' Feld 'id' | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan migrate | php artisan migrate | ||
</syntaxhighlight> | </syntaxhighlight> | ||
= Modell Note erzeugen = | |||
* Ein Model ist eine Klasse, die eine DB-Tabelle verwaltet. | * Ein Model ist eine Klasse, die eine DB-Tabelle verwaltet. | ||
* Konvention: die Tabelle ist kleingeschrieben und in Mehrzahl. Die Klasse ist großgeschrieben und in Einzahl. | * Konvention: die Tabelle ist kleingeschrieben und in Mehrzahl. Die Klasse ist großgeschrieben und in Einzahl. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan make:model Note | php artisan make:model Note | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Es entsteht die Datei app/Models/Note.php | * Es entsteht die Datei app/Models/Note.php | ||
< | <syntaxhighlight lang="php"> | ||
class Note extends Model | class Note extends Model | ||
{ | { | ||
Zeile 287: | Zeile 342: | ||
protected $fillable = ['title', 'body', 'user_id']; | protected $fillable = ['title', 'body', 'user_id']; | ||
} | } | ||
</ | </syntaxhighlight> | ||
* in der Variable $fillable werden die Felder aufgeführt, die in Formularen vorkommen. | * in der Variable $fillable werden die Felder aufgeführt, die in Formularen vorkommen. | ||
== Model User mit Note verknüpfen == | == Model User mit Note verknüpfen == | ||
* Wir wollen nur Notizen anzeigen, die dem angemeldeten Benutzer gehören. Dazu ergänzen wir das von Laravel definierte Modul User | |||
* Wir wollen nur Notizen anzeigen, die dem angemeldeten Benutzer gehören. Dazu ergänzen wir das von Laravel definierte Modul User. | |||
* Datei app/Models/User.php | * Datei app/Models/User.php | ||
< | |||
<syntaxhighlight lang="php"> | |||
public function usersNotes(){ | public function usersNotes(){ | ||
// related Model: Note, field in users: user_id | // related Model: Note, field in users: user_id | ||
return $this->hasMany(Note::class, 'user_id'); | return $this->hasMany(Note::class, 'user_id'); | ||
} | } | ||
</ | </syntaxhighlight> | ||
* Klasse Note importieren (Doppelklick auf Note, Alt-Ctrl-I) | * Klasse Note importieren (Doppelklick auf Note, Alt-Ctrl-I) | ||
== Formular Notiz erzeugen == | == Formular Notiz erzeugen == | ||
* Datei resources/views/home.blade.php: | * Datei resources/views/home.blade.php: | ||
< | <syntaxhighlight lang="html"> | ||
@auth | @auth | ||
<form action="/logout" method="POST"> | <form action="/logout" method="POST"> | ||
Zeile 319: | Zeile 379: | ||
</form> | </form> | ||
@else | @else | ||
</ | </syntaxhighlight> | ||
* | |||
* Controller erstellen: | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
php artisan make:controller NoteController | php artisan make:controller NoteController | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Es entsteht die Datei app/Http/controllers/NoteController | * Es entsteht die Datei app/Http/controllers/NoteController | ||
< | <syntaxhighlight lang="php"> | ||
class NoteController extends Controller | class NoteController extends Controller | ||
{ | { | ||
Zeile 341: | Zeile 404: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
* Es findet eine Validierung der 2 Felder statt. | * Es findet eine Validierung der 2 Felder statt. | ||
* <code>strip_tags($incomingFields['titles']);</code> dient der Sicherheit: Sonst kann durch Benutzereingaben Blödsinn gemacht werden. | * <code>strip_tags($incomingFields['titles']);</code> dient der Sicherheit: Sonst kann durch Benutzereingaben Blödsinn gemacht werden. | ||
Zeile 348: | Zeile 412: | ||
* Datei routes/web.php: | * Datei routes/web.php: | ||
< | |||
<syntaxhighlight lang="php"> | |||
</ | Route::post('/create-note', [NoteController::class, 'createNote']); | ||
</syntaxhighlight> | |||
* Klasse NoteController importieren: Doppelklick auf NoteController, Alt-Ctrl-I | * Klasse NoteController importieren: Doppelklick auf NoteController, Alt-Ctrl-I | ||
* Browser: F5 | * Browser: F5 | ||
Zeile 356: | Zeile 422: | ||
** Titel und Notiz eintragen, Button "Create Note" | ** Titel und Notiz eintragen, Button "Create Note" | ||
* Im Terminal: | * Im Terminal: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
sudo mysql dbnotes <<EOS | sudo mysql dbnotes <<EOS | ||
Zeile 361: | Zeile 428: | ||
EOS | EOS | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Es wird ein Datensatz angezeigt. | * Es wird ein Datensatz angezeigt. | ||
== | == Alle Notizen anzeigen == | ||
< | * Es sollen alle Notizen des angemeldeten Benutzers angezeigt werden. | ||
* In resources/views/home.blade.php: Einfügen '''nach''' dem Formular für "Create Note": | |||
<syntaxhighlight lang="html"> | |||
<div style="border: 3px solid black;"> | |||
<h2>All Posts</h2> | |||
@csrf | |||
@foreach($notes as $note) | |||
<div style="background-color: lightgray; padding: 10px; margins: 10px;"> | |||
<h3>{{$note['title']}}</h3> | |||
{{$note['body']}} | |||
<p><a href="/edit-note/{{$note->id}}">Edit</a></p> | |||
<form action="/delete-note/{{$note->id}}" method="POST"> | |||
@csrf | |||
@method('DELETE') | |||
<button>Delete</button> | |||
</form> | |||
</div> | |||
@endforeach | |||
</div> | |||
</syntaxhighlight> | |||
* Mit der "Anotation" @foreach wird eine Schleife gemacht, mit der alle Datensätze unter dem Namen $note abgeliefert werden. | |||
* Für jeden Datensatz wird ein Abschnitt gebaut, der Titel und Notiz anzeigt. | |||
** Der Link "Edit" enthält eine URL mit der Id der Notiz. | |||
** Die Aktions-URL in der Form für den Löschbutton enthält die Id der Notiz. Das ist zur Unterscheidung notwendig. | |||
* in routes/web.php: | |||
<syntaxhighlight lang="php"> | |||
Route::get('/', function () { | Route::get('/', function () { | ||
$notes = null; | |||
if (auth()->check()) { | if (auth()->check()) { | ||
// $posts = Note::all(); | // $posts = Note::all(); | ||
Zeile 375: | Zeile 472: | ||
return view('home', ['notes' => $notes]); | return view('home', ['notes' => $notes]); | ||
}); | }); | ||
</ | </syntaxhighlight> | ||
* Wenn wir angemeldet sind (<code>auth()->check()</code>) holen wir alle Notizen des Benutzers aus der Datenbank und übergeben diese Liste der View "home": | |||
** <code>auth()->user()</code> liefert den aktuellen Benutzer. | |||
** <code>->usersNotes()</code> liefert alle Einträge des Benutzers in der Tabelle notes | |||
** <code>->latest()</code> sortiert so dass die jüngsten Notizen zuerst kommen | |||
** <code>->get()</code> liefert die Datensätze als Liste: latest() liefert einen Container. | |||
* Im Browser: F5 Jetzt sollte die eine Notiz angezeigt werden. Noch eine Notiz erzeugen, es werden dann 2 angezeigt. | |||
= Edieren einer Notiz = | |||
* Die angezeigten Notizen enthalten einen Link auf eine Route /edit-note/<note_id> | |||
* Wir brauchen einen Handler für diese Route: | |||
* /routes/web.php: | |||
<syntaxhighlight lang="php"> | |||
Route::get('/edit-note/{note}', [NoteController::class, 'showEditScreen']); | |||
</syntaxhighlight> | |||
* Die "Variable" {note} korrespondiert automatisch mit dem Namen des Parameters der Funktion showEditScreen(): | |||
** Es erfolgt automatisch eine Zuweisung an die Instanz des Models (Klasse "Note") mit der passenden Id. | |||
* app/Http/Controllers/NoteController: | |||
<syntaxhighlight lang="php"> | |||
public function showEditScreen(Note $note){ | |||
return view('edit-note', ['note' => $note]); | |||
} | |||
</syntaxhighlight> | |||
* Neue Datei: resources/view/edit-note.blade.php: | |||
* "!" eintragen, es erscheint ein "... emmet abbrevation", dieses anklicken. Dann: | |||
<syntaxhighlight lang="html"> | |||
... | |||
<body> | |||
<h1>Edit Note</h1> | |||
<form action="/update-note/{{$note->id}}" method="POST"> | |||
@csrf | |||
@method('PUT') | |||
<input type="text" name="title" value="{{$note->title}}" placeholder="Title"> | |||
<textarea name="body" placeholder="Body">{{$note->body}} | |||
</textarea> | |||
<button>Save Changes</button> | |||
</form> | |||
</body> | |||
... | |||
</syntaxhighlight> | |||
* Browser F5: Jetzt funktioniert die Anzeige der zu edierenden Notiz. | |||
=== Speichern der geänderten Notiz === | |||
* /routes/web.php: | |||
<syntaxhighlight lang="php"> | |||
Route::put('/update-note/{note}', [NoteController::class, 'updateNote']); | |||
</syntaxhighlight> | |||
* In der View ist die Methode "PUT" spezifiert, daher muss das Routing mit put() erfolgen. | |||
* app/Http/Controllers/NoteController: | |||
<syntaxhighlight lang="php"> | |||
public function updateNote(Note $note, Request $request){ | |||
if (auth()->user()->id !== $note['user_id']){ | |||
return redirect('/'); | |||
} | |||
$incomingFields = $request->validate([ | |||
'title' => 'required', | |||
'body' => 'required' | |||
]); | |||
$incomingFields['title'] = strip_tags($incomingFields['title']); | |||
$incomingFields['body'] = strip_tags($incomingFields['body']); | |||
$note->update($incomingFields); | |||
return redirect('/'); | |||
} | |||
</syntaxhighlight> | |||
== Löschen der Notiz == | |||
Es gibt schon einen Button in der Notiz. Dieser ist umrahmt von einer Form mit der Aktions-URL /delete-note/<note_id>. | |||
* /routes/web.php: | |||
<syntaxhighlight lang="php"> | |||
Route::delete('/delete-note/{note}', [NoteController::class, 'deleteNote']); | |||
</syntaxhighlight> | |||
* Die "Variable" {note} korrespondiert automatisch mit dem Namen des Parameters der Funktion deleteNote(): | |||
** Es erfolgt automatisch eine Zuweisung an die Instanz des Modells mit der passenden Id. | |||
* app/Http/Controllers/NoteController: | |||
<syntaxhighlight lang="php"> | |||
public function deleteNote(Note $note){ | |||
if (auth()->user()->id === $note['user_id']){ | |||
$note->delete(); | |||
} | |||
return redirect('/'); | |||
} | |||
</syntaxhighlight> | |||
* Wir löschen nur, wenn die zu löschende Notiz dem angemeldete Benutzer gehört. |
Aktuelle Version vom 31. Dezember 2023, 12:37 Uhr
Links
- Inspiriert von https://www.youtube.com/watch?v=cDEVWbz2PpQ (englisch)
- ApplikationMitNginx
Zielsetzung
Es wird eine Webapplikation "Notes" gebaut, die folgende grundlegende Fähigkeiten hat:
- Registrierung mit Name, Email und Passwort. Speichern in einer Datenbank.
- Login mit Name und Passwort, Abgleich mit Datenbank
- Logout
- Erstellen einer Notiz mit Titel und Nachricht
- Anzeige aller Notizen des Benutzers
- Edieren der Notiz
- Löschen der Notiz
Voraussetzungen
- Visual Studio Code ist installiert, ebenso die Erweiterungen
- PHP,
- PHP Namespace Resolver,
- Laravel Blade Snippets
- Composer ist installiert.
apt update && apt install composer
Erstellen des Projekts
USER=ck
PROJ=notes
BASE=/home/ws/php/$PROJ
sudo mkdir -p $BASE
sudo chown USER $BASE
cd $BASE
composer create-project laravel/laravel .
- Damit wird ein Verzeichnis $BASE/notes erstellt, und mittels Composer das Grundgerüst der Applikation erstellt.
- Es steht dann das PHP-Script artisan zur Verfügung, mit dem Laravel-Kommandos gegeben werden können.
- Interessant ist auch die Datei .env, in der die Konfiguration der Applikation stattfindet.
Starten des Webservers
Es gibt einen eingebauten Webserver für die Entwicklung:
cd $BASE
./artisan serve
Die Applikation steht dann im Browser unter http://localhost:8000 zur Verfügung.
Modifizieren der Homepage
Wir öffnen den Editor "Visual Studio Code" in dem Projektverzeichnis:
cd $BASE
code .
- Die erstellte Homepage steht in der Datei resources/views/welcome.blade.php
- Diese Datei öffnen und im Quelltext was ändern
- Im Browser F5 drücken, der geänderte Text wird sichtbar
Neue Homepage erstellen
- Neue Datei: resources/views/home.blade.php Wichtig: endet immer mit .blade.php
- In VSCode ganz links oben "Explorersymbol" (2 Dokumente) anklicken, so dass die Dateien sichtbar sind
- Verzeichnis resources durch anklicken aufklappen, dort Verzeichnis "views" anklicken.
- Oben gibt es ein Dokumentsymbol mit "+": Dieses anklicken, Namen "home.blade.php" eintragen, RETURN.
- In der leeren Datei:
- Eintippen: ! Es erscheint ein Icon "Schraubenschlüssel" mit Titel "!". Dieses anklicken. Damit wird ein HTML-Grundgerüst erstellt.
- Titel korigieren: <title>Notes</title>
- Im Body:
<h1>Test</h1>
- Speichern
- Datei routes/web.php
Route::get('/', function () {
return view('welcome');
});
- Hier steht der Aufruf der Datei "welcome", was für "welcome.blade.php" steht.
- Wir ersetzen das durch unsere eigene Datei "home.blade.php" mittels Eintrag von "home":
Route::get('/', function () {
return view('home');
});
- Im Browser aktualisieren (F5): und wir sehen die neue Homepage.
Das Registrier-Formular
- resources/views/home.blade.php: Füge in den body-Block ein.
<div style="border: 3px solid black;">
<h2>Register</h2>
<form action="/register" method="POST">
@csrf
<input name="name" type="text" placeholder="name">
<input name="email" type="text" placeholder="email">
<input name="password" type="password" placeholder="password">
<button>Register</button>
</form>
</div>
- @csrf: Sicherheit: https://en.wikipedia.org/wiki/Cross-site_request_forgery
- /routes/web.php:
Route::post('/register', function() { return 'We have registered'; });
- Browser aktualisieren (F5)
- Auf Button "Register" klicken
UserController erstellen
- Die Behandlung des Formulars passiert in einem "Controller".
- Diesen können wir erzeugen, indem wir im Terminal (im Editor gibt es unten ein Registerblatt "TERMINAL"):
php artisan make:controller UserController
- Es entsteht die Datei app/Http/Controllers/UserController
- Wir erweitern die Klasse UserController um eine Methode register(), die die Registrierung vornimmt:
class UserController extends Controller
{
public function register(Request $request) {
$incomingFields = $request->validate([
'name' => ['required', 'min:3', 'max:10', Rule::unique('users', 'name')],
'email' => ['required', 'email', Rule::unique('users', 'email')],
'password' => 'required'
]);
return 'Hello from UserController';
}
}
- Die Methode bekommt den Parameter $request, der die HTML-Infos der Seite enthält.
- Der Aufruf von $request->validate() stellt Überprüfungen an:
- In dem assoziativen Feld werden die zu überprüfenden Felder mit den Validierungsregeln verknüpft:
- Das Feld "name" darf nicht leer sein ("required"), der Name muss mindestens 3 und darf maximal 10 Zeichen lang sein, der Name darf noch nicht in der DB in der Tabelle 'users' im Feld 'name' vorkommen.
- Im Editor ist die Klasse Rule rot unterkringelt: Das symbolisiert einen Fehler:
- Damit PHP die Klasse Rules in der Datei UserController.php kennt: Doppelklick auf Rule (Auswählen), dann Alt-Ctr-I (Import Class). In der Liste Illuminate\validation\Rule auswählen.
- In der Datei routes/web.php:
Wir ersetzen die Route::post(...)
Zeile mit:
Route::post('/register', [UserController::class, 'register']);
- Mit der URL /register wird die Methode 'register' in der Klasse UserController aufgerufen.
- Damit PHP die Klasse UserController in der Datei web.php kennt: UserController doppelklicken (Auswählen): Rechte Maustaste: "Import Class" (oder Alt-Ctr-I). Es wird dann der Import erledigt.
- Browser mit F5 aktualisieren:
- Nichts eingeben, auf Button "Register" klicken: nichts passiert
- Alle 3 Felder ausfüllen und dann Button "Register" klicken: Fehlermeldung, dass Benutzer nicht angemeldet ist. Das ist so korrekt, die Validierung ist demnach korrekt durchlaufen.
Datenbank
Datenbank anlegen
- Mit mysql die Datenbank dbnotes mit Benutzer notes und Passwort 'TopSecret' anlegen, z.B. mit
sudo mysql mysql <<EOS
CREATE DATABASE dbnotes;
GRANT ALL ON dbnotes.* to 'notes'@'localhost' IDENTIFIED BY 'TopSecret';
EOS
- In der Datei .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dbnotes
DB_USERNAME=notes
DB_PASSWORD='TopSecret'
Datenbank initialisieren
- Laravel hat schon einige DB-Elemente vorbereitet. Diese werden so aktiviert:
php artisan migrate
mysql -u notes -pTopSecret dbnotes <<EOS
show tables;
select * from users;
EOS
- Es werden die Tabellen failed_jobs migrations password_reset_tokens personal_access_tokens und users angelegt.
- Die Tabelle users ist leer.
Registrierung fertigstellen
- Änderung in app/Http/UserController.php:
public function register(Request $request) {
$incomingFields = $request->validate([
'name' => ['required', 'min:3', 'max:10', Rule::unique('users', 'name')],
'email' => ['required', 'email', Rule::unique('users', 'email')],
'password' => 'required'
]);
$incomingFields['password'] = bcrypt($incomingFields['password']);
$user = User::create($incomingFields);
auth()->login($user);
return redirect('/');
}
- "User::" doppelklicken, Alt-Ctrl-i importiert die Klasse App\Models\User
- Browser: F5, Felder ausfüllen, Button "Register"
mysql -u notes -pTopSecret dbnotes <<EOS
select * from users;
EOS
- Jetzt ist ein Datensatz in der Datenbank, der passende Benutzer
- Fülle das Formular nochmal mit anderen Daten aus, damit 2 Benutzer existieren.
Logout und Login
- In resources/home.blade.php:
<body>
@auth
<form action="/logout" method="POST">
@csrf
<p>You are logged in!</p>
<button>logout</button>
</form>
@else
<div style="border: 3px solid black;">
<h2>Register</h2>
<form action="/register" method="POST">
@csrf
<input name="name" type="text" placeholder="name">
<input name="email" type="text" placeholder="email">
<input name="password" type="password" placeholder="password">
<button>Register</button>
</form>
</div>
<div style="border: 3px solid black;">
<h2>Login</h2>
<form action="/login" method="POST">
@csrf
<input name="loginname" type="text" placeholder="name">
<input name="loginpassword" type="password" placeholder="password">
<button>Login</button>
</form>
</div>
@endauth
</body>
- Änderung in app/Http/UserController.php:
public function logout()
{
auth()->logout();
return redirect('/');
}
public function login(Request $request)
{
$incomingFields = $request->validate([
'loginname' => 'required',
'loginpassword' => 'required'
]);
if (
auth()->attempt([
'name' => $incomingFields['loginname'],
'password' => $incomingFields['loginpassword']
])
) {
$request->session()->regenerate();
}
return redirect('/');
}
- Änderung in routes/web.php:
Route::post('/register', [UserController::class, 'register']);
Route::post('/login', [UserController::class, 'login']);
Route::post('/logout', [UserController::class, 'logout']);
- im Browser: F5 Felder für Login ausfüllen, Button "Login" -> "Your are logged in"
Tabelle für Notizen erstellen
Wir brauchen einen nichtflüchtigen Speicher für die Notizen, also eine Datenbanktabelle. Der Name sei notes. (Konvention: Kleinschreibung, Mehrzahl)
php artisan make:migration create_notes_table
- Der Teil zwischen create_ und _table ist der Name der Tabelle, also "notes".
- Es wird eine Datei database/migrations/xxx_create_notes_table.php erzeugt: Diese edieren.
public function up(): void
{
Schema::create('notes', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('title');
$table->longText('body');
$table->foreignId('user_id')->constrained();
});
}
- Es werden folgende Felder angelegt:
- 'id': der Primärschlüssel, erzeugt durch $table->id()
- 'changed_at' und 'updated_at', erzeugt durch $table->timestamps()
- 'title': ein Feld vom Typ VARCHAR(255)
- 'body': ein Feld vom Type LONGTEXT
- 'user_id': Ein Fremdschlüssel auf die Tabelle 'users' Feld 'id'
php artisan migrate
Modell Note erzeugen
- Ein Model ist eine Klasse, die eine DB-Tabelle verwaltet.
- Konvention: die Tabelle ist kleingeschrieben und in Mehrzahl. Die Klasse ist großgeschrieben und in Einzahl.
php artisan make:model Note
- Es entsteht die Datei app/Models/Note.php
class Note extends Model
{
use HasFactory;
// editable fields:
protected $fillable = ['title', 'body', 'user_id'];
}
- in der Variable $fillable werden die Felder aufgeführt, die in Formularen vorkommen.
Model User mit Note verknüpfen
- Wir wollen nur Notizen anzeigen, die dem angemeldeten Benutzer gehören. Dazu ergänzen wir das von Laravel definierte Modul User.
- Datei app/Models/User.php
public function usersNotes(){
// related Model: Note, field in users: user_id
return $this->hasMany(Note::class, 'user_id');
}
- Klasse Note importieren (Doppelklick auf Note, Alt-Ctrl-I)
Formular Notiz erzeugen
- Datei resources/views/home.blade.php:
@auth
<form action="/logout" method="POST">
@csrf
<p>You are logged in!</p>
<button>logout</button>
</form>
<div style="border: 3px solid black;">
<h2>Create a New Note</h2>
<form action="/create-note" method="POST">
@csrf
<input type="text" name="title" placeholder="note title">
<textarea type="text" name="body" placeholder="body content...">
</textarea>
<button>Save Note</button>
</form>
@else
- Controller erstellen:
php artisan make:controller NoteController
- Es entsteht die Datei app/Http/controllers/NoteController
class NoteController extends Controller
{
public function createNote(Request $request)
{
$incomingFields = $request->validate([
'title' => 'required',
'body' => 'required'
]);
$incomingFields['title'] = strip_tags($incomingFields['title']);
$incomingFields['body'] = strip_tags($incomingFields['body']);
$incomingFields['user_id'] = auth()->id();
Note::create($incomingFields);
return redirect('/');
}
}
- Es findet eine Validierung der 2 Felder statt.
strip_tags($incomingFields['titles']);
dient der Sicherheit: Sonst kann durch Benutzereingaben Blödsinn gemacht werden.$incomingFields['user_id'] = auth()->id();
speichert die Id des angemeldeten Benutzers im Datensatz.- Klasse Note importieren: Note doppelklicken, Alt-Ctrl-I
- Datei routes/web.php:
Route::post('/create-note', [NoteController::class, 'createNote']);
- Klasse NoteController importieren: Doppelklick auf NoteController, Alt-Ctrl-I
- Browser: F5
- Wenn nicht angemeldet: Anmelden
- Titel und Notiz eintragen, Button "Create Note"
- Im Terminal:
sudo mysql dbnotes <<EOS
select * from notes;
EOS
- Es wird ein Datensatz angezeigt.
Alle Notizen anzeigen
- Es sollen alle Notizen des angemeldeten Benutzers angezeigt werden.
- In resources/views/home.blade.php: Einfügen nach dem Formular für "Create Note":
<div style="border: 3px solid black;">
<h2>All Posts</h2>
@csrf
@foreach($notes as $note)
<div style="background-color: lightgray; padding: 10px; margins: 10px;">
<h3>{{$note['title']}}</h3>
{{$note['body']}}
<p><a href="/edit-note/{{$note->id}}">Edit</a></p>
<form action="/delete-note/{{$note->id}}" method="POST">
@csrf
@method('DELETE')
<button>Delete</button>
</form>
</div>
@endforeach
</div>
- Mit der "Anotation" @foreach wird eine Schleife gemacht, mit der alle Datensätze unter dem Namen $note abgeliefert werden.
- Für jeden Datensatz wird ein Abschnitt gebaut, der Titel und Notiz anzeigt.
- Der Link "Edit" enthält eine URL mit der Id der Notiz.
- Die Aktions-URL in der Form für den Löschbutton enthält die Id der Notiz. Das ist zur Unterscheidung notwendig.
- in routes/web.php:
Route::get('/', function () {
$notes = null;
if (auth()->check()) {
// $posts = Note::all();
// or:
// $posts = Note::where('user_id', auth()->id()->get());
// or:
$notes = auth()->user()->usersNotes()->latest()->get();
}
return view('home', ['notes' => $notes]);
});
- Wenn wir angemeldet sind (
auth()->check()
) holen wir alle Notizen des Benutzers aus der Datenbank und übergeben diese Liste der View "home":auth()->user()
liefert den aktuellen Benutzer.->usersNotes()
liefert alle Einträge des Benutzers in der Tabelle notes->latest()
sortiert so dass die jüngsten Notizen zuerst kommen->get()
liefert die Datensätze als Liste: latest() liefert einen Container.
- Im Browser: F5 Jetzt sollte die eine Notiz angezeigt werden. Noch eine Notiz erzeugen, es werden dann 2 angezeigt.
Edieren einer Notiz
- Die angezeigten Notizen enthalten einen Link auf eine Route /edit-note/<note_id>
- Wir brauchen einen Handler für diese Route:
- /routes/web.php:
Route::get('/edit-note/{note}', [NoteController::class, 'showEditScreen']);
- Die "Variable" {note} korrespondiert automatisch mit dem Namen des Parameters der Funktion showEditScreen():
- Es erfolgt automatisch eine Zuweisung an die Instanz des Models (Klasse "Note") mit der passenden Id.
- app/Http/Controllers/NoteController:
public function showEditScreen(Note $note){
return view('edit-note', ['note' => $note]);
}
- Neue Datei: resources/view/edit-note.blade.php:
- "!" eintragen, es erscheint ein "... emmet abbrevation", dieses anklicken. Dann:
...
<body>
<h1>Edit Note</h1>
<form action="/update-note/{{$note->id}}" method="POST">
@csrf
@method('PUT')
<input type="text" name="title" value="{{$note->title}}" placeholder="Title">
<textarea name="body" placeholder="Body">{{$note->body}}
</textarea>
<button>Save Changes</button>
</form>
</body>
...
- Browser F5: Jetzt funktioniert die Anzeige der zu edierenden Notiz.
Speichern der geänderten Notiz
- /routes/web.php:
Route::put('/update-note/{note}', [NoteController::class, 'updateNote']);
- In der View ist die Methode "PUT" spezifiert, daher muss das Routing mit put() erfolgen.
- app/Http/Controllers/NoteController:
public function updateNote(Note $note, Request $request){
if (auth()->user()->id !== $note['user_id']){
return redirect('/');
}
$incomingFields = $request->validate([
'title' => 'required',
'body' => 'required'
]);
$incomingFields['title'] = strip_tags($incomingFields['title']);
$incomingFields['body'] = strip_tags($incomingFields['body']);
$note->update($incomingFields);
return redirect('/');
}
Löschen der Notiz
Es gibt schon einen Button in der Notiz. Dieser ist umrahmt von einer Form mit der Aktions-URL /delete-note/<note_id>.
- /routes/web.php:
Route::delete('/delete-note/{note}', [NoteController::class, 'deleteNote']);
- Die "Variable" {note} korrespondiert automatisch mit dem Namen des Parameters der Funktion deleteNote():
- Es erfolgt automatisch eine Zuweisung an die Instanz des Modells mit der passenden Id.
- app/Http/Controllers/NoteController:
public function deleteNote(Note $note){
if (auth()->user()->id === $note['user_id']){
$note->delete();
}
return redirect('/');
}
- Wir löschen nur, wenn die zu löschende Notiz dem angemeldete Benutzer gehört.