LaravelTutorial: Unterschied zwischen den Versionen

Aus Vokabulabor
Zur Navigation springen Zur Suche springen
 
(22 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>


= Vorgehen =
= Erstellen des Projekts =
== 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 **artisan** zur Verfügung, mit dem Laravel-Kommandos gegeben werden können.
* 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 ==
= 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 ==
= 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 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: &lt;title>Notes&lt;/title>
* Titel korigieren: &lt;title>Notes&lt;/title>
* Im Body:
* Im Body:
<pre>
<syntaxhighlight lang="html">
<h1>Test</h1>
<h1>Test</h1>
</pre>
</syntaxhighlight>
 
* Speichern
* Speichern
* Datei routes/web.php
* Datei routes/web.php
<pre>
 
<syntaxhighlight lang="php">
Route::get('/', function () {
Route::get('/', function () {
     return view('welcome');
     return view('welcome');
});
});
</pre>
</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.
<pre>
* 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');
});
});
</pre>
</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.


== Das Registrier-Formular ==
<syntaxhighlight lang="html">
* resources/views/home.blade.php
<pre>
<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>
</pre>
</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:
<pre>
<syntaxhighlight lang="php">
Route::post('/register', function() { return 'We have registered'; });
Route::post('/register', function() { return 'We have registered'; });
</pre>
</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"):


== UserController erstellen ==
* Die Behandlung des Formulars passiert in einem "Controller"
<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:
<pre>
 
<syntaxhighlight lang="php">
class UserController extends Controller
class UserController extends Controller
{
{
Zeile 107: Zeile 137:
     }
     }
}
}
</pre>
</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  
<pre>
<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
</pre>
</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 ==
= Registrierung fertigstellen =
* Änderung in app/Http/UserController.php:
* Änderung in app/Http/UserController.php:
<pre>
<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('/');
     }
     }
</pre>
</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 ==
= Logout und Login =
 
* In resources/home.blade.php:
* In resources/home.blade.php:
<pre>
 
<syntaxhighlight lang="html">
<body>
<body>
@auth
@auth
Zeile 211: Zeile 254:
@endauth
@endauth
</body>
</body>
</pre>
</syntaxhighlight>
 
* Änderung in app/Http/UserController.php:
* Änderung in app/Http/UserController.php:
<pre>
 
<syntaxhighlight lang="php">
     public function logout()
     public function logout()
     {
     {
Zeile 225: Zeile 270:
             'loginpassword' => 'required'
             'loginpassword' => 'required'
         ]);
         ]);
        $incomingFields['loginpassword'] = bcrypt($incomingFields['loginpassword'] . $incomingFields['loginname']);
         if (
         if (
             auth()->attempt([
             auth()->attempt([
Zeile 236: Zeile 280:
         return redirect('/');
         return redirect('/');
     }
     }
</pre>
</syntaxhighlight>
 
* Änderung in routes/web.php:
* Änderung in routes/web.php:
<pre>
 
<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']);
</pre>
</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.
<pre>
 
<syntaxhighlight lang="php">
     public function up(): void
     public function up(): void
     {
     {
Zeile 262: Zeile 313:
         });
         });
     }
     }
</pre>
</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 ==
= 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
<pre>
<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'];
}
}
</pre>
</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
<pre>
 
<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');
   }
   }
</pre>
</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:
<pre>
<syntaxhighlight lang="html">
@auth
@auth
   <form action="/logout" method="POST">
   <form action="/logout" method="POST">
Zeile 319: Zeile 379:
     </form>
     </form>
@else
@else
</pre>
</syntaxhighlight>
* Modell erstellen:
 
* 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
<pre>
<syntaxhighlight lang="php">
class NoteController extends Controller
class NoteController extends Controller
{
{
Zeile 341: Zeile 404:
     }
     }
}
}
</pre>
</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:
<pre>
 
route::post('/create-note', [NoteController::class, 'createNote']);
<syntaxhighlight lang="php">
</pre>
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 ==
== Alle Notizen anzeigen ==
* Es sollen alle Notizen des angemeldeten Benutzers angezeigt werden.
* Es sollen alle Notizen des angemeldeten Benutzers angezeigt werden.
* In resources/views/home.blade.php: Einfügen **nach** dem Formular für "Create Note":
* In resources/views/home.blade.php: Einfügen '''nach''' dem Formular für "Create Note":
<pre>
<syntaxhighlight lang="html">
   <div style="border: 3px solid black;">
   <div style="border: 3px solid black;">
     <h2>All Posts</h2>
     <h2>All Posts</h2>
Zeile 375: Zeile 443:
         {{$note['body']}}
         {{$note['body']}}
         <p><a href="/edit-note/{{$note->id}}">Edit</a></p>
         <p><a href="/edit-note/{{$note->id}}">Edit</a></p>
         <form action="/delete-note" method="POST">
         <form action="/delete-note/{{$note->id}}" method="POST">
           @csrf
           @csrf
           @method('DELETE')
           @method('DELETE')
Zeile 383: Zeile 451:
     @endforeach
     @endforeach
   </div>
   </div>
</pre>
</syntaxhighlight>
 
* Mit der "Anotation" @foreach wird eine Schleife gemacht, mit der alle Datensätze unter dem Namen $note abgeliefert werden.
* 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.
* 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:
* in routes/web.php:
<pre>
 
<syntaxhighlight lang="php">
Route::get('/', function () {
Route::get('/', function () {
    $notes = null;
     if (auth()->check()) {
     if (auth()->check()) {
         // $posts = Note::all();
         // $posts = Note::all();
Zeile 398: Zeile 472:
     return view('home', ['notes' => $notes]);
     return view('home', ['notes' => $notes]);
});
});
</pre>
</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":
* 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>auth()->user()</code> liefert den aktuellen Benutzer.  
Zeile 407: Zeile 482:
* Im Browser: F5 Jetzt sollte die eine Notiz angezeigt werden. Noch eine Notiz erzeugen, es werden dann 2 angezeigt.
* Im Browser: F5 Jetzt sollte die eine Notiz angezeigt werden. Noch eine Notiz erzeugen, es werden dann 2 angezeigt.


== Edieren einer Notiz ==
= Edieren einer Notiz =
 
* Die angezeigten Notizen enthalten einen Link auf eine Route /edit-note/&lt;note_id>
* Die angezeigten Notizen enthalten einen Link auf eine Route /edit-note/&lt;note_id>
* Wir brauchen einen Handler für diese Route:
* Wir brauchen einen Handler für diese Route:
web.php:
* /routes/web.php:
<pre>
 
Route::put('/edit-note/{$note}', [NoteController::class, 'updateNote']);
<syntaxhighlight lang="php">
</pre>
Route::get('/edit-note/{note}', [NoteController::class, 'showEditScreen']);
* NoteController:
</syntaxhighlight>
<pre>
 
* 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){
     public function updateNote(Note $note, Request $request){
         if (auth()->user()->id !== $note['user_id']){
         if (auth()->user()->id !== $note['user_id']){
Zeile 424: Zeile 541:
             'body' => 'required'
             'body' => 'required'
         ]);
         ]);
         $incomingFields['title'] = strip_tags($incomingFields['titles']);
         $incomingFields['title'] = strip_tags($incomingFields['title']);
         $incomingFields['body'] = strip_tags($incomingFields['body']);
         $incomingFields['body'] = strip_tags($incomingFields['body']);
         $note->update($incomingFields);
         $note->update($incomingFields);
         return redirect('/');
         return redirect('/');
       }
       }
</pre>
</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/&lt;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

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>
  • /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.