Immer wieder ertappe ich mich, wie ich beim Start eines neues Projektes nach denselben Dingen suche. Dabei könnte ich sie auch einfach einmal hier aufschreiben, dann hätte ich sie gleich.

1. Domain-Einstellungen (Optional)

Da diese immer etwas Zeit brauchen, bis sie sich im Internet herumgesprochen haben, konfiguriere ich diese zuerst. Die Frage ist durchaus berechtigt, weshalb bei der Einrichtung der Entwicklungsumgebung eine Domain angelegt wird. Die Antwort ist, das entsprechend dem agilen Ansatz ich auch gleich zu Beginn das Projekt auf der produktiven Umgebung testen möchte, damit ich dann nicht überrascht werden von irgendwelchen Punkten die fehlen und ich dann in meinem “fertigen” Code mit Fehlersuche beginnen muss.

All-Inkl Nameserver-Einstellungen für extern registrierte Domains, dabei gilt es:

  1. Die Domain im KAS von All-inkl anzulegen und danach
  2. Die Nameserver beim externen Domainanbieter anzupassen, mit den nachfolgenden Einstellungen:
1. ns5.kasserver.com
2. ns6.kasserver.com

2. Updates installieren

Insbesondere wenn die Umgebung nur sporadisch genutzt wird, können sich eine Vielzahl von Update / Aktualisierung angestaut haben, die nicht nur aus Sicherheitsgründen sondern auch für verbesserte Kompatibilitäten installiert werden sollten:

sudo apt update -y && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo apt clean -y && sudo apt autoclean -y

// Quelle: https://askubuntu.com/questions/733434/one-single-command-to-update-everything-in-ubuntu

3. Ordnerstruktur erstellen

Manche Dateien, wie bspw. htpasswd, sollten nicht direkt im Root-Verzeichnis des jeweiligen Hosts liegen um sie vor Zugriffen zu schützen, sondern eine Ebene höher. Daher bevorzuge ich innerhalb meiner Projektverzeichnisse (bspw. projekt1) noch ein htdocs-Verzeichnis anzulegen. Das Verzeichnis könnte auch “www” oder “public” heißen, ich bevorzuge “htdocs”. Unabhängig davon, ob die Dateistruktur von Laravel selbst auch nochmals eine Zwischenebene einzieht.

www/projekt1/htdocs/

4. Host-Eintrag anlegen

Früher gab es einen Host, der mittels 127.0.0.1 erreichbar war und meine einzelnen Projekte haben sich in verschiedenen Ordnern befunden. Oftmals kam es dann bei Überführung in die produktive Umgebung zu Probleme, weil Pfad-Angaben nicht gestimmt haben, etc. Zudem ist es unter Windows auch sehr aufwendig, weil zunächst Notepad als Administrator gestartet werden muss und dann die Datei im Dateisystem gesucht werden muss (C:\Windows\System32\drivers\etc\hosts), aber diese auch nur angezeigt wird, wenn der Filter für *.txt-Dateien deaktiviert ist. Nach dem Speichern ist dann auch nicht ganz klar ob die Änderung sofort gilt oder erstmal die Netzwerkschnittstelle neugestartet werden muss.

Da ist es unter Linux deutlich einfacher, mit einem Editor der Wahl die Datei aufrufen und entsprechend ergänzen. Funktioniert in der Regel sofort.

// Datei: /etc/hosts
127.0.0.1    www.projekt1.de

4.1 Host-Eintrag testen

Wer gleich schon sicher gehen will, ob der Hosts-Eintrag auch berücksichtigt wird, kann mittels “dig” schauen:

$ dig www.projekt1.de

// Erwartete Antwort
;; ANSWER SECTION:
www.projekt1.de.	0	IN	A	127.0.0.1

// Zum Vergleich
$ dig web.de
// Antwort Vorher
;; ANSWER SECTION:
web.de.		5	IN	A	82.165.229.138
web.de.		5	IN	A	82.165.230.17

// nachdem wir web.de in Hosts-Datei eingetragen haben
;; ANSWER SECTION:
web.de.		0	IN	A	127.0.0.1

5. Apache VirtualHost (vHost) Datei anlegen

Apache-Konfigurationsdatei erstellen im Unterordner “sites-available”. Ob die Datei jetzt eine “100” davor hat oder “200” ist egal. Es geht auch ohne die Zahlen, nur mit diesen ist die Reihenfolge offensichtlich geklärt, was bei umfangreicheren Konfiguration wichtig sein kann. Da bei einer Anfrage der Apache die Dateien der Reihe nach durchsucht um die Anfrage zuzuordnen.

In Vorbereitung auf das Laravel-Projekt, ist am “DocumentRoot” Pfad ein “/public” angehängt. Dies wird durch Laravel automatisch erzeugt und ist der Startpunkt für Aufrufe:

// Datei: 100-projekt1.conf

<VirtualHost *:80>
        ServerName www.projekt1.de
        ServerAdmin webmaster@localhost
        DocumentRoot /home/www/projekt1/htdocs
/public

        <Directory "/home/www/projekt1/htdocs">
                Require all granted
                AllowOverride All
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/projekt1_error.log
        CustomLog ${APACHE_LOG_DIR}/projekt1_access.log combined
</VirtualHost>

Hinweis#1: mit AllowOverride All gestatten wir bspw. .htaccess-Dateien die Konfiguration zu erweitern / überschreiben. Laravel bringt im Public-Verzeichnis eine .htaccess-Datei mit, die essentiell für die Funktionsweise ist.

Hinweis: das Skript “a2ensite” muss genutzt werden, denn die alleinige Platzierung eines Symlinks im Ordner “sites-enabled” ist nicht ausreichend:

$ a2ensite 100-projekt1.conf
$ systemctl reload apache2

Danach sollte im Apache-Verzeichnis “sites-enabled” ein Symlink existieren

$ 100-projekt1.conf -> ../sites-available/100-projekt1.conf

5.1 Apache Konfiguration Testen

Um die Apache Konfiguration zu Testen, kann jetzt einfach in das Unterverzeichnis “public” eine “index.html” gelegt werden, bspw. mit dem Inhalt “Hallo Welt!!!”

// Datei: /home/www/projekt1/htdocs
/public/index.html

<h1>Hallo Welt!!!</h1>

Wenn jetzt beim Aufruf von www.projekt1.de in großen Buchstaben in eurem Browser ein Hallo Welt!!! erscheint, wißt ihr, das bis hier die Konfiguration funktioniert.

6. Laravel-Projekt einrichten

Als nächsten Schritt installieren wir mittels Composer das Laravel Projekt. Dazu muss das Verzeichnis komplett leer sein, worauf euch Composer ggf. auch nochmals hinweisen wird.

// Punkt am Ende für das aktuelle Verzeichnis
$ cd /home/www/projekt1/htdocs
$ composer create-project laravel/laravel .

Wichtig: der Punkt (.) nach laravel/laravel richtet das Projekt direkt im aktuellen Verzeichnis ein und erstellt nicht nochmals ein Unterverzeichnis.

6.1 Test im Browser

UnexpectedValueException
The stream or file "/home/www/projekt1/htdocs/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied

Berechtigungen anpassen, damit der Apache Prozess auch in das Logfile schreiben kann:

$ ps aux | egrep '(apache|httpd)'
www-data    7953  0.1  0.2 231700 38596 ?        S    12:07   0:00 /usr/sbin/apache2 -k start
// Der User ist "www-data" also geben wir ihm den Ordner
// https://stackoverflow.com/questions/30639174/how-to-set-up-file-permissions-for-laravel hier ist auch noch der bootstrap/cache Ordner aufgeführt, also nehmen wir den auch noch mit
$ sudo chown -R www-data:www-data storage bootstrap/cache

Hinweis: Ich hatte mir hier das Leben leicht gemacht, letztlich führte es dann dazu, das Befehle die mit “php artisan” auf der Konsole direkt abgesetzt werden nicht korrekt ausgeführt werden können. Da der lokale Benutzer, wenn der User www-data ist, keinen Zugriff auf den Ordner hat. Somit habe ich dem Ordner doch 777-Rechte gegeben. Bei einer Entwicklungsumgebung halte ich das für vertretbar, im produktiven Betrieb nicht, dann bitte wie im oben erwähnten Artikel vorgehen.

7. Git einrichten

// Lokal: ins Verzeichnis wechseln
$ git init
// Github Remote Repo "RepoPro1" einrichten
// Lokal: Ziel für Push definieren
$ git remote add origin git@github.com:user/RepoPro1.git
// Lokal: Branch setzen
$ git push --set-upstream origin master

Hinweis: Den Branch “master” gibt es bei Github standardmäßig nicht mehr.

8. Datenbank einrichten

Eine Datenbank entweder direkt über die Konsole einrichten oder über phpMyAdmin o.ä. als Kollation habe ich “utf8mb4_german2_ci” ausgewählt und als Engine “InnoDB”. Anschließend die Datenbank Zugangsdaten in der .env-Datei eintragen.

sudo mysql
mysql> CREATE DATABASE projekt1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> CREATE USER 'projekt1user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, REFERENCES ON projekt1.* TO 'projekt1user'@'localhost';

8.1 Datenbank-Schnittstelle testen

Am einfachsten läßt sich die Schnittstelle mittels dem folgenden Aufruf testen:

$ php artisan migrate:fresh

9. Laravel Starter Kit einrichten (Optional)

Wer in seinem Projekt eine Registrierung- und Login-Funktionalität benötigt und auch noch Tailwind CSS verwenden will, der sollte ich Laravel Breeze anschauen (https://laravel.com/docs/8.x/starter-kits).

10. IDE Erweiterungen installieren

Es lohnt sich immer wieder mal nach neuen Erweiterungen für die IDE zu schauen, die das Leben ein bisschen einfacher machen. Bspw. gibt es auch eine Erweiterung für Tailwind, welche CSS-Klassen vervollständigt und dabei auch noch eine kleine Beschreibung anzeigt.

11. Developer Toolbar installieren

Egal ob direkt im Projekt installierbar oder im Browser, für fast jede Sprache gibt es Helferlein, die das Debuggen vereinfachen. Für Laravel kann ich die Laravel Debugbar von barryvdh empfehlen.

$ composer require barryvdh/laravel-debugbar --dev

Es empfiehlt sich, wie in der Anleitung beschrieben die config/app.php zu erweitern:

'providers' => [
...
Barryvdh\Debugbar\ServiceProvider::class,
]
// und
'aliases' => [
...
'Debugbar' => Barryvdh\Debugbar\Facade::class,
]

Weitere Konfigurationen können in der debugbar.php vorgenommen werden, die sich nach dem Absetzen des folgenden Befehls im Config Ordner befindet:

$ php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
Copied File [/vendor/barryvdh/laravel-debugbar/config/debugbar.php] To [/config/debugbar.php]
Publishing complete.

Genutzt werden kann die Debugbar mit folgender use-Direktive:

use Barryvdh\Debugbar\Facade as Debugbar;
...
Debugbar::info("Message");

Weitere Einzelheiten finden sich direkt auf Github. Diese sind sehr hilfreich, wenn man nicht immer dd() einsetzen möchte.

12. Model erstellen

Ein Modell mit “migration” und “controller” zu erstellen, kann der folgende Aufruf verwendet werden.

$ php artisan make:model Projekt -mc
// oder mit Seeder und Factory (gleich für Testzwecke)
$ php artisan make:model Projekt -mcsf 

Hinweis: der Name des Models ist nach Laravel-Konvention im Singular (Einzahl) zu vergeben. Laravel wandelt dies bei der Erstellung der Migration dann automatisch in den Plural (Mehrzahl) um, also bspw. aus “Facility” wird “Facilities”.

12.1 Model-Struktur

Ich meine mich zu erinnern, dass früher im Model gleich die passenden Methoden angelegt wurden. Kann mich aber auch irren, von daher gleich mal als Bild (https://laravel.com/docs/8.x/controllers#actions-handled-by-resource-controller) anbei:

Wie das ganze dann in der /routes/web.php auszusehen kann:

Route::get('/photos', 'PhotoController@index')->name('photos.index');
Route::post('/photos', 'PhotoController@store')->name('photos.store');
Route::get('/photos/{photo}/edit', 'PhotoController@edit')->name('photos.edit');

// Basierend auf der Dokumenation zu 8.x https://laravel.com/docs/8.x/controllers#basic-controllers

Route::get('/user/{id}', [UserController::class, 'show'])->name('user.show');

// Letzte Variante hat den Vorteil, dass bei Umbenennung der Klasse diese auch von der IDE gefunden wird.

Hinweis: der Unterschied zwischen {photo} und {id} nennt sich Implicit Binding (https://laravel.com/docs/5.8/routing#implicit-binding). Dabei erzeugt Laravel automatisch bei Übergabe einer ID das entsprechende Objekt und übergibt dieses an die Methode.

Im Controller sieht das ganze dann wie folgt aus:

public function update(Photo $photo)
{
   $domain->update($this->validatePhoto());

   return redirect($photo->path());
}  

Letzterer Aufruf ist eine sehr praktische und leicht zu implementierende Methode im Model:

public function path()
{
    return route('photos.show', $this);
}

Hier schließt sich der Kreis wieder, weshalb es sinnvoll ist, seine Routen zu benennen (photos.show).

Laravel: Soft Deleting

Eine sehr nützliche Funktionalität die auch noch vergleichsweise einfach zu implementieren ist, ist das Soft Deleting (https://laravel.com/docs/8.x/eloquent#soft-deleting).

// Im jeweiligen Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;


class Car extends Model
{
    use HasFactory;
    use SoftDeletes;


    public function Wheels()
    {
        return $this->hasMany(Wheel::class);
    }
}
=====================================
// und in der Migration
    public function up()
    {
        Schema::create('cars', function (Blueprint $table) {
            $table->id();
            $table->softDeletes();

        });
    }

Laravel: Caches Clear All

Beim Aufruf von php artisan erscheint eine Liste mit verschiedenen Befehlen, u.a. auch die Möglichkeit zum “clearen” verschiedenster Caches. Was jedoch nicht erscheint, ist die Möglichkeit bzw. der Befehl zum leeren aller Caches gleichzeitig:

# php artisan optimize:clear
Compiled views cleared!
Application cache cleared!
Route cache cleared!
Configuration cache cleared!
Compiled services and packages files removed!
Caches cleared successfully!

Sozusagen sechs auf einen Streich.