Developer Blog

Tipps und Tricks für Entwickler und IT-Interessierte

Laravel | Cookbook

Routing

Alle Routen anzeigen

php artisan route:list

Routen dynamisch erzeugen

composer require illuminate/support
use Illuminate\Support\Facades\File;

function generateRoutes($basePath, $baseNamespace = 'Pages', $routePrefix = '/')
{
    $files = File::allFiles($basePath);

    foreach ($files as $file) {
        $relativePath = str_replace([$basePath, '.vue'], '', $file->getRelativePathname());
        $routeName = str_replace(DIRECTORY_SEPARATOR, '.', $relativePath);
        $routeUri = str_replace(DIRECTORY_SEPARATOR, '/', $relativePath);

        // Example: if file is `resources/js/Pages/Examples/layout-discord.vue`
        // $routeName = 'Examples.layout-discord';
        // $routeUri = 'examples/layout-discord'

        Route::get($routePrefix . $routeUri, function () use ($relativePath, $baseNamespace) {
            return Inertia::render($baseNamespace . str_replace('/', '\\', $relativePath));
        })->name($routeName);
    }
}

generateRoutes(resource_path('js/Pages'));

Mail / SMTP

Lokaler Mailserver für SMTP Testing

MailHog: Web and API based SMTP testing

Vue3 and Laravel + Inertia | Cookbook

Allgemeines

Vue and CSS

Styling with CSS Variables

<script setup>
const theme = {
    "menu": {
        "background": 'black',
        "item": {
            "background": "green"
        },
        "subitem": {
            "background": "green"
        }
    }
}
</script>
<style scoped>
.menu {
    background-color: v-bind('theme.menu.background');
}

Using PrimeVue

Installation

❯ pnpm add primevue @primevue/themes
❯ pnpm add primevue @primevue/icons

Laravel | Erstellen eines API Backend

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Notwendige Komponenten eines API Backend

Für die Umsetzung eines API Backend benötigen wir in unserer Laravel App mehrere Komponenten:

  • ein Model, das beschreibt, wir die Daten aussehen, die unsere API verwendet
  • ein Controller, der den Programmcode beinhaltet, m die einzelnen Aktionen (Create, Read, Update, Delete, ..) auszuführen
  • weitere Komponenten, um die Nutzung zu erleichern
    • ein Seeder um die Datenbank mit definierte Werten zu füllen
    • ein Migrationsskript, um die notwendigen Datenbankeinträge (Tabelle, Spalten) zu erstellen
    • Testskripte, um unseren Controller und weitere Kompoentne zu prüfen

Hinweis

Vorab ein Hinweis: jeder der nachfolgenden Kommandos erstellen Komponenten in Form von Dateien. Ein erneutes Erstellen wird fehlschlagen, da die entsprechende Datei schon vorhanden ist.

Um mit den verschiedenen Kommandos zu experimentieren, verwenden sie am besten eine Funktionalität von Git, die es ihnen ermöglicht de Status zurückzusetzen, also Dateien zu löschen.

Wenn Sie ein Kommando ausgeführt haben, dann sehen sie im Visual Studio die neu erstellen Dateien:

Selektieren sie die neu erstellten Dateien und wählen sie dann die Option Discard Changes:

Komponenten erstellen

Jede dieser Komponenten kann einzeln erstellt werden, z. B mit

❯ php artisan make:model Post
❯ php artisan make:controller PostController

Hierbei gilt es natürlich, die Abhängigkeiten zu berücksichtigen. Der Controller muss mit dem Model arbeiten und sollte daher die gleichen Felder und Feldnamen verwendet.

Einfacher ist es daher, das Laravel diese Abhängigkeiten kennt und entsprechend verwendet.

Bei der Erstellung des Models können wir auch einen dazu passenden Controller und weitere Komponenten erstellen:

❯ php artisan make:model Post --migration --controller --seed --api --requests --pest

   INFO  Model and test [app/Models/Post.php] created successfully.
   INFO  Migration [database/migrations/2024_04_19_073500_create_posts_table.php] created successfully.
   INFO  Seeder [database/seeders/PostSeeder.php] created successfully.
   INFO  Request [app/Http/Requests/StorePostRequest.php] created successfully.
   INFO  Request [app/Http/Requests/UpdatePostRequest.php] created successfully.
   INFO  Controller and test [app/Http/Controllers/PostController.php] created successfully.

Dies erstellt die Dateien

	app/Http/Controllers/PostController.php
	app/Http/Requests/
	app/Models/Post.php
	database/migrations/2024_04_19_073500_create_posts_table.php
	database/seeders/PostSeeder.php
	tests/Feature/Http/
	tests/Feature/Models/

Und um alle möglichen Komponenten zu erstellen, verwenden wir den nachfolgenden Befehl:

❯ php artisan make:model Post --all --pest

   INFO  Model and test [app/Models/Post.php] created successfully.
   INFO  Factory [database/factories/PostFactory.php] created successfully.
   INFO  Migration [database/migrations/2024_04_19_073800_create_posts_table.php] created successfully.
   INFO  Seeder [database/seeders/PostSeeder.php] created successfully.
   INFO  Request [app/Http/Requests/StorePostRequest.php] created successfully.
   INFO  Request [app/Http/Requests/UpdatePostRequest.php] created successfully.
   INFO  Controller and test [app/Http/Controllers/PostController.php] created successfully.
   INFO  Policy [app/Policies/PostPolicy.php] created successfully.

Dies erstellt die Dateien

	app/Http/Controllers/PostController.php
	app/Http/Requests/
	app/Models/Post.php
	app/Policies/PostPolicy.php
	database/factories/PostFactory.php
	database/migrations/2024_04_19_075242_create_posts_table.php
	database/seeders/PostSeeder.php
	tests/Feature/Http/
	tests/Feature/Models/

Jetzt aber genug der Einführung. Lassen Sie uns mit dem Erstellen des API Backend beginnen.

Sollten Sie Komponentne erstellen haben, dann löschen Sie diese wie beschrieben.

Einrichten der Starter-App

Erstellen der Starter App. In diesem Post beschreibe ich, wie sie eine Starter-App erstellen und die grundlegenden Anpassungen durchführen können.

Sie können die Starter App auch mit dem Laravel-Installer erstellen:

❯ laravel new App   --jet 
                   --api 
                   --database sqlite 
                   --stack livewire  
                   --teams 
                   --dark 
                   --verification 
                   --git 
                   --pest

Oder auch direkt mit dem dazugehörigen Repository starten:

❯ git clone https://github.com/r14r/Laravel_Tutorial_Erstellen-einer-Starter-App App
❯ cd App
❯ composer install
❯ npm install
❯ npm run build
❯ cp .env.example  .env
❯ php artisan migrate
❯ php artisan key:generate
❯ php artisan serve

Erstellen der erforderlichen Komponenten

❯ php artisan make:model Post --all --pest

   INFO  Model and test [app/Models/Post.php] created successfully.
   INFO  Factory [database/factories/PostFactory.php] created successfully.
   INFO  Migration [database/migrations/2024_04_19_075600_create_posts_table.php] created successfully.
   INFO  Seeder [database/seeders/PostSeeder.php] created successfully.
   INFO  Request [app/Http/Requests/StorePostRequest.php] created successfully.
   INFO  Request [app/Http/Requests/UpdatePostRequest.php] created successfully.
   INFO  Controller and test [app/Http/Controllers/PostController.php] created successfully.
   INFO  Policy [app/Policies/PostPolicy.php] created successfully.

Anpassen des Routing

Erweiteren Sie die Datei routes/api.php um den nachfolgenden Programmcode (Zeile 6 und 12-14):

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

use App\Http\Controllers\PostController;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

Route::get('/post',         [PostController::class, 'index']);
Route::post('/post',        [PostController::class, 'store']);
Route::delete('/post/{id}', [PostController::class, 'destroy']);

Prüfen wir die von Laravel erkannten Routen:

Erster Test

Prüfen wir die bisherigen Schritte. Starten sie Laravel und öffnen sie einen Browser unter der Adresse http://127.0.0.1:8000/api/post

❯ php artisan serve

   INFO  Server running on [http://127.0.0.1:8000].

  Press Ctrl+C to stop the server

Sie sehen, das sie nichts sehen. Was zu erwarten war, da die API noch keine Daten hat und daher auch keine Daten liefern kann.

Datenbank

Tabelle erstellen

Erweitern wir als nächstes das Migrations-Skript, um die notwendige Tabelle zu erstellen und mit Werten zu füllen.

Die entsprechende Datei ist

database/migrations/2024_04_19_075600_create_posts_table.php 

Der genaue Name kann dabei bei Ihnen abweichen.

Passen sie die Funktion up() an:

public function up(): void
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
}

Die neuen Felder werden über eine Migration in der Datenbank erstellt:

❯ php artisan migrate

Wir kontrollieren, ob die Tabelle richtig erstellt wurde:

❯ php artisan db
SQLite version 3.37.0 2021-12-09 01:34:53
Enter ".help" for usage hints.
sqlite> PRAGMA table_info(posts);
0|id|INTEGER|1||1
1|title|varchar|1||0
2|content|TEXT|1||0
3|created_at|datetime|0||0
4|updated_at|datetime|0||0
sqlite> .quit

Tabelle füllen mit Hilfe eines Seeder

Erstellen Sie den Seeder, falls die Datei database/seeders/PostSeeder.php nicht vorhanden ist:

php artisan make:seeder PostSeeder

Ersetzen Sie den Inhalt mit dem nachfolgenden Programmcode:

<?php

namespace Database\Seeders;

use Illuminate\Support\Facades\DB;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    static $posts = [
        ['title' => 'Summer Health', 'content' => 'Stay hydrated!'],
        ['title' => 'Pasta Recipe',  'content' => 'Tomato base'],
        ['title' => 'Decor Ideas',   'content' => 'Go minimal'],
        ['title' => 'Python Tips',   'content' => 'Use lists']
    ];

    public function run(): void
    {
        foreach (self::$posts as $post) {
            DB::table('posts')->insert([
                'title' => $post['title'], 'content' => $post['content']
            ]);
        }
    }
}

Starten Sie den Seeder:

❯ php artisan db:seed --class PostSeeder

Hinweis: Sie können diesen Seeder auch die den generellen DatabaseSeeder mit aufnehmen, so das der Name des Seeders nicht mit angegeben werden muss.

Fügen die in der Datei database/seeders/DatabaseSeeder.php den nachfolgenden Programmcode hinzu:

$this->call([
    PostSeeder::class,
]);

Starten Sie den Seeder:

❯ php artisan db:seed

Bearbeiten der Komponenten

Model

Erweitern Sie die Datei app/Models/Post.php um die zusätzlichen Felder:

protected $fillable = ['title', 'content', 'created_at', 'updated_at'];

Testing

Für die Operationen GET (Einträge anzeigen), POST (neuen Eintrag erstellen) und DELETE (Eintrag löschen) stehen jeweils eigene URLs zur Verfügung. Am einfachsten ist der Test der URLs mit Postman.

Testen mit Postman

GET

http://127.0.0.1:8000/api/get

POST

http://127.0.0.1:8000/api/post

DELETE

http://127.0.0.1:8000/api/delete/6        

Testen mit curl in der Kommandozeile

GET

❯ curl http://127.0.0.1:8000/api/post
[{"id":1,"title":"Summer Health","content":"Stay hydrated!","created_at":null,"updated_at":null},{"id":2,"title":"Pasta Recipe","content":"Tomato base","created_at":null,"updated_at":null},{"id":3,"title":"Decor Ideas","content":"Go minimal","created_at":null,"updated_at":null},{"id":4,"title":"Python Tips","content":"Use lists","created_at":null,"updated_at":null}]

POST

❯ curl -X POST  http://127.0.0.1:8000/api/post -H "Content-Type: application/json" -d '{ "title": "Neuer Titel", "content": "Neuer Inhalt!" }'
{"title":"Neuer Titel","content":"Neuer Inhalt!","updated_at":"2024-04-19T15:48:22.000000Z","created_at":"2024-04-19T15:48:22.000000Z","id":6}

DELETE

❯ curl -X DELETE http://127.0.0.1:8000/api/post/6
{"message":"Post deleted"}        

Laravel | Erstellen eines Blade Layouts

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Einrichten der Starter-App

Erstellen der Starter App. In diesem Post beschreibe ich, wie sie eine Starter-App erstellen und die grundlegenden Anpassungen durchführen können.

Sie können auch direkt mit dem dazugehörigen Repository starten:

❯ git clone https://github.com/r14r/Laravel_Tutorial_Erstellen-einer-Starter-App App
❯ cd App
❯ composer install
❯ npm install
❯ npm run build
❯ cp .env.example  .env
❯ php artisan migrate
❯ php artisan key:generate
❯ php artisan serve

Motivation

Betrachten wir die Dashboard-Seite der Starter-App: dashboard.blade.php. Es fällt auf, das nur der eigentliche Inhalt definiert wird (z. B. der Willkommens-Text in Zeile 11). Das eigentliche “Aussehen” (Layout) wird abgeleitet von dem generellen App-Layout. Dies wird durch die folgende erste Zele erreicht:

<x-app-layout>

Wir wollen Seiten entwicklen, die ein unterschiedliches Aussehen haben. Daher wollen wir neben dem generellen App-Layout noch weitere Layouts erstellen.

Ein weiteres Layout soll z. B. eine Seitenleiste erhalten, wir wollen es AppSidebarLayout nennen.

Verwendet wird es dann, wenn als erste Zeile die folgende verwendet wird:

<x-app-sidebar-layout>

Neue Seite erstellen

Im ersten Schritt erstellen wir zuerst eine neue Seite

Blade-Datei für neue Seite erstellen

Kopieren sie die Datei dashboard.blade.php nach page_with_sidebar.blade.php.

Löschen sie den Aufruf des Willkommens-Text in Zeile 11:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
            {{ __('Dashboard') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg">
            </div>
        </div>
    </div>
</x-app-layout>

Navigationsleiste erweitern

Erstellen Sie einen weiteren Navigationslink in der Datei resources/views/navigation-menu.blade.php:

<x-nav-link href="{{ route('page_with_sidebar') }}" :active="request()->routeIs('page_with_sidebar')">
    {{ __('Page with Sidebar') }}
</x-nav-link>

Wenn Sie jetzt die Seite aufrufen, dann bekommen Sie einen Fehler: die Route wurde nicht gefunden

Routing hinzufügen

Erweitern Sie die Datei routes/web.php und die neue Route:

 Route::get('/page_with_sidebar', function () {
        return view('page_with_sidebar');
 })->name('page_with_sidebar');

Ergebnis

So sieht dann unsere Seite aus:

Neues Layout verwenden

Nun ändern wir das Layout unserer neuen Seite in Zeile 1 und Zeile 14

<x-app-sidebar-layout>

Wenn Sie jetzt alles speichern und die Seite aktualisieren (im Browser) bekommen sie wie zu Erwarten eine Fehlermeldung:

Lassen Sie uns also das neue Layout erstellen.

Neues Layout erstellen

Kopieren der Datei resources/views/layouts/app.blade.php nach resources/views/layouts/app_with_sidebar.php.

Laravel | Arbeiten mit PowerGrid

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Einrichten der Starter-App

Erstellen der Starter App.

Sie können dazu dieses Kommando verwenden:

❯ laravel new site --jet --teams --api --pest --dark --database sqlite --stack livewire --verification --git

Dieser Post arbeitet mit der hier beschriebenen StarterApp. Der Post beschreibt, wie eine Starter-App erstellt und die grundlegenden Anpassungen durchgeführt werden. Sie können auch direkt mit dem dazugehörigen Repository starten:

❯ git clone https://github.com/r14r/Laravel_Tutorial_Erstellen-einer-Starter-App Laravel_Tutorial_Arbeiten-mit-PowerGrid
❯ Laravel_Tutorial_Arbeiten-mit-PowerGrid
❯ composer install
❯ npm install
❯ npm run build
❯ cp .env.example  .env
❯ php artisan migrate
❯ php artisan key:generate
❯ php artisan serve

Installation

1. via Composer

composer require power-components/livewire-powergrid

2. Konfigurationsdatei bereitstellen

php artisan vendor:publish --tag=livewire-powergrid-config

Die erstelle Konfigurationsdatei liegt hier: config/livewire-powergrid.php.

3. Views und Sprachdateien bereitstellen (Optional)

Hinweis: Überspringen sie diesen Schritt, wenn die keine Anpassung der Views durchführen wollen.

Views bereitstellen:

php artisan vendor:publish --tag=livewire-powergrid-views

Sprachdateien bereitstellen:

php artisan vendor:publish --tag=livewire-powergrid-lang

Laravel | Erstellen einer Blade Komponente

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Einrichten der Starter-App

Erstellen der Starter App. In diesem Post beschreibe ich, wie sie eine Starter-App erstellen und die grundlegenden Anpassungen durchführen können.

Sie können auch direkt mit dem dazugehörigen Repository starten:

❯ git clone https://github.com/r14r/Laravel_Tutorial_Erstellen-einer-Starter-App Laravel_Tutorial_Arbeiten-mit-htmx
❯ composer install
❯ npm install
❯ npm run build
❯ cp .env.example  .env
❯ php artisan migrate
❯ php artisan key:generate
❯ php artisan serve

Motivation

Betrachten wir sie Startseite der Starterapp: welcome.blade.php. Es fällt auf, das es viele Code-Wiederholungen gibt, z. B bei den Überschriften

1013: <h2 class="text-xl font-semibold text-black dark:text-white">Laracasts</h2>
1047: <h2 class="text-xl font-semibold text-black dark:text-white">Laravel News</h2>
1077: <h2 class="text-xl font-semibold text-black dark:text-white">Vibrant Ecosystem</h2>

Nicht nur, das mehrfach den gleichen Code geschrieben wurde, auch bei Änderungen muss an mehreren Stellen geändert werden.

Dabei sollte aus Designer-Sicht das Layout oder Aussehend der Seite aus “logischen” Bestandteilen bestehen, z. B. einer Überschrift, einer Kopfzeile, einen Textabschnitt.

Das aussehen dieser Bestandteile wird dann an andere Stelle festgelegt, aber jeder dieser Bestandteile hat das gleiche Aussehen.

Unsere Startseite würde dann vielleicht so aussehen:

<starter-header>Laracasts</starter-header>
<starter-header>Laravel News</starter-header>
<starter-header>Vibrant Ecosystem</starter-header>

Fügen wird diesen Bestandteile auch noch Parameter hinzu, so vereinfacht sich die Startseite so:

<starter-header title="Laracasts"/>
<starter-header title"Laravel News" />
<starter-header title="Vibrant Ecosystem" />

Um dies zu implementieren, setzen wir Blade Komponenten ein.

Hinweis: Der Begriff Blade kommt ihnen vielleicht bekannt vor. Sie haben ihn bestimmt schon in den Dateinamen gesehen:

welcome.blade.php

Was ist Blade.

Die Projektseite formuliert es so:

Blade is the simple, yet powerful templating engine that is included with Laravel.

Oder, wie Wikipedia es formuliert:

Funktional erweiterbare Templating-Sprache zur Erstellung von Views.

Blade ist Funktional erweiterbare Templating-Sprache zur Erstellung von Views..
Eine Template-Engine (von englisch für Vorlage bzw. Schablone und Maschine) ist eine Software, die eine Vorlagen-Datei (engl. Template) verarbeitet und bestimmte Platzhalter darin ähnlich wie bei einem Formular durch jeweils aktuelle Inhalte ersetzt. 

Unsere Blade besteht also nicht nur aus grafischen Elementen, z. B. Text, Icon, Auswahlbox. Sondern auch aus Programmkomponenten um diese Seite mit dynamischen Werten zu füllen.

Bezogen auf unser Beispiel des <starter-header> besteht der dynamische Teil aus dem tatächlich anzuzeugendem Text. Dadurch kann diese Komponente an beliebiger Stelle eingesetzt werden und jedesmal einen anderen Text darstellen, immer mit dem gleichen Aussehen.

Erstellen wir also unsere erste Komponente.

Blade Komponente erstellen

Wir erstellen eine Komponente mit dem nachfolgenden Kommando:

❯ php artisan make:component StarterHeader

Dadurch werden zwei neue Dateien erstellt:

  • resources/views/components/starter-header.blade.php
    In dieser Datei wird das Aussehen der Komponente festgelegt, Dabei können zusätzlich “Variablen” (also die dynamischen Teile) verwende werden.
  • app/View/Components/StarterHeader.php
    Diese ist die zum Blade-View zugehöroge PHP-Klasse. In dieser Daten wird die Verbindung zwischen dem Komponentennamen (starter-header) und dem dazugehörigen View definiert. Zusätzlich werden hier auch die dynamischen Teile festgelegt, also die Parameter für unsere Komponente (im Beispiel “title”)

Blade Komponente konfigurieren

PHP Klasse: app/View/Components/StarterHeader.php

Betrachten wird im ersten Schritt die PHP Klasse der Komponente: app/View/Components/StarterHeader.php

  • Der Name der Klasse in Zeile 9: StarterHeader
  • Den dazugehörigen Blade-View in Zeile 24: components.starter-header

Fügen wir nun einen Parameter für unsere Komponente hinzu: title

    public $title;

    public function __construct($title = 'Header')
    {
        $this->title = $title;
    }

Wird in einer Blade-Seite einen Komponenten verwendet, so wird der Konstruktor (__construct) dieser Komponente aufgerufen, um den notwendigen Programmcode zu erstellen.

Die beim Aufruf angegebene Parameter werden diesem Konstruktor übergeben.

Der Aufruf:

<starter-header title=Laravel" />

führt zu einem Aufruf des Konstruktor __construct("Laravel"). Der Parameter $title wird dabei durch den angegebene Wert "Laravel" ersetzt.

Blade View: resources/views/components/starter-header.blade.php

Betrachten wir nun den Blade-View in der Datei resources/views/components/starter-header.blade.php:

Der View hat noch sehr wenig Inhalt. Wir ersetzen nun den vorhandenen Inhalt mit dem Inhalt aus der Datei welcome.blade.php, der für die Formatierung und Darstellung es Headers verwendet wird:

Wir sehen zwei Header: in Zeile 1013 und in Zeile 1047. Beide beinhalten die gleichen HTML-Anweisungen und unterscheiden sich nur im Text des Headers:

<h2 class="text-xl font-semibold text-black dark:text-white">Laracasts</h2>

Wir kopieren also diese Anweisungen in unseren Blade-View. Dabei ersetzen wir gleichzeitig den (statischen) Text, z. B. Laracast, durch unsere Variable $title, die den gewüscnhten Titel enthält.

Hinweis: Damit Blade erkenne kann, wann HTML Code verwendet wird und wann Programmcode, wird dieser durch die Zeichen {{ und }} umschlossen.

In unserer Seite welcome.blade.php können wir nun die Komponente einsetzen und den vorhandenen Text ersetzen:

Wichtig: Beachten Sie, das wir dem Komponentennamen mit x- beginnen müssen:

<x-starter-header title="Vibrant Ecosystem" />

Laravel | Erstellen einer Starter-App

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Installation

Erstellen der Starter App

laravel new Laravel-Starter-App --jet --stack=livewire --api --teams --dark --pest --verification --database sqlite --git --quiet

Konfiguration der Datenbank

Die Konfiguration der Datenbank erfolgt in der bereits vorhandenen Datei .env.

Nach der Installation finden Sie dort die folgenden Einträge für eine MySQL Datenbankanbindung:

Datenbank SQLite verwenden

Bei Einrichten der Starter App wurde bereits SQLite vorselektiert:

DB_CONNECTION=sqlite

Leere Datenbankdatei erstellen

Die Starter App hat bereits eine leeren Datenbank im Ordner database mit dem Namen database.sqlite

Tabellen erstellen

Die für Laravel notwendigen Tabellen wurden ebenfalls eingerichtet:

php artisan migrate
Migration der Tabellen durchführen

Hinweis: Die vorhandenen Tabellen können Sie sie über die Kommandozeile anzeigen:

sqlite3.exe .\database\database.sqlite ".tables"
Vorhandene Tabellen in der Datenbank

Installation und Konfiguration des Frontend

Um mit dem Frontend arbeiten zu können, müssen zu ersten die notwendigen Komponenten installiert und das Frontend erstellt werden:

npm install
npm run dev

Anzeigen der Anwendung

Die grundlegende Installation und Konfiguration ist abgeschlossen. Starten Sie die Anwendung durch das nachfolgende Kommando:

php artisan serve
Lokalen Webserver starten

Öffnen Sie die Anwendung im Browser über den Link http://127.0.0.1:8000

Anmeldung und Authentifizierung

Die erstelle Laravel-Anwendung enthält bereits die Funktionalität, das sich Benutzer Registrieren und Anmelden können. Hierzu finden Sie auf der Startseite rechts oben entsprechende Links.

Mit den verwendeten Standardeinstellungen sin Ausreichend für das Einrichten neuen Benutzer.

Um eine höhere Sicherheit zu erreichen, werden diese Einstellungen so geändert, das einen Bestätigungsmail an den Benutzer gesendet wird. Erst durch den Klick auf den darin enthaltenen Bestätigungslink wird die Einrichtung des Benutzers abgeschlossen.

E-Mail Verification einrichten

Das Einrichten dieser E-Mail Verification erfolgt in mehreren Schritten.

Hinweis: Diese wurden ebenfalls durch die Starter App durchführt.

Aktivieren des Features emailVerification in der Datei config/fortify.php

Einrichtern der E-Mail-Verifizierung bei der Registrierung

Passen sie die Datei app/User.php an:

use Illuminate\Contracts\Auth\MustVerifyEmail;
class User extends Authenticatable implements MustVerifyEmail
Einrichten der emailVerification

Festlegen der E-Mail Konfiguration

Damit Laravel eine E-Mail versenden kann wird der Zugang zu einem E-Mail Server verwendet. Hier benötigen wir die Zugangsdaten für den SMTP-Versand.

Für das Empfangen der Verifizierungsmail gibt es zwei Möglichkeiten:

  • die Verifizierungsmail wird im Laravel-Log gespeichert
  • die Verifizierungsmail wird an einen SMPT-Server gesenden
    • sie können hier ihren eigenen SMTP-Server verwenden
    • oder sie verwenden mailpit (Lokaler SMTP-Server für Tests)

Festgelegt werden diese Parameter wieder in der Datei .env:

Die entsprechenden Werte hängen vom verwendeten Mailserver ab.

Laravel Log verwenden

Der Eintrag in der Datei .env lautet dann:

MAIL_MAILER=log

Die Verifizierungsmail finden sie dann in der Datei storage/logs/laravel.log:

Eigener SMTP-Server

Der Eintrag in der Datei .env lautet dann:

MAIL_MAILER=smtp

Zuerst müssen Sie bei ihrem Provider ein Mailkonto einrichten (Benutzer und Passwort). Testen Sie am besten diese Daten über den von ihrem Provider bereitgestellten Webmailer.

Die weiteren Werte (Port, Protokoll) entnehmen Sie der Dokumentation ihres Providers.

Nachfolgend zwei Beispiele:

ProviderStratoUnited Domains
MAIL_MAILERsmtpsmtp
MAIL_HOSTsmtp.strato.desmtps.udag.de
MAIL_PORT587587
MAIL_ENCRYPTIONtlstls
Beispiele für die E-Mail-Konfiguration

Lokaler SMTP-Server für Testzwecke: mailpit

Für Testzwecke können sie die E-Mail-Konfiguration auch vereinfachen, indem ein lokaler Mailserver verwendet wird: mailpit (https://mailpit.axllent.org/docs/install/)

Installieren sie diesen und starten sie ihn dann mit den Parametern aus der Laravel-Konfiguration: dem SMTP-Port aus der Datei .env. Passen sie zusätzlich den Parameter MAIL_MAILER an:

MAIL_MAILER=smtp
❯ mailpit -s 0.0.0.0:2525
INFO[2024/04/09 18:42:33] [smtpd] starting on 0.0.0.0:2525 (no encryption)
INFO[2024/04/09 18:42:33] [http] starting on [::]:8025
INFO[2024/04/09 18:42:33] [http] accessible via http://localhost:8025/

mailpit Frontend öffnen

Klicken sie auf den angegeben Link der Ausgabe von mailpit, um das Frontend zu starten:

http://localhost:8025/

Benutzer einrichten

Starten Sie die Anwendung neu und richten Sie einen weiteren Benutzer ein

php artisan serve
Lokalen Webserver starten

Registrierung durchführen

Starten Sie die Registrierung über den Link Register auf der Startseite.

Geben Sie die notwendigen Benutzerdaten ein. Bestätigen Sie ebenfalls die Terms of Services, falls diese aktiviert sind. Wie sie diese aktivieren können sie hier nachlese.

Bei erfolgreichen Versand der Bestätigungsmail erscheint diese Anzeige. Sie erhalten ebenfalls eine Bestätigungsmail an die verwendete E-Mail-Adresse.

Öffnen Sie die E-Mail und klicken Sie auf den Bestätigungslink. Achten Sie darauf, das der Link im gleichen Browser geöffnet wird, mit dem Sie die Registrierung durchgeführt haben.

Alternativ kopieren Sie einfach den Link, wechseln wieder zurück in das Registrierungsfenster und fügen den kopierten Link ein.

Wenn sie mit mailpit arbeiten, so sehen sie im Frontend, das eine neue E-Mail eingegangen ist:

Danach befinden Sie sich auf dem Dashboard der Anwendung. Die Registrierung hat somit funktioniert.

Mögliche Konfigurationsfehler

E-Mail Server ist falsch

Fehlermeldung: Der angegebene Host ist unbekannt

Port ist falsch

Fehlermeldung: Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte

Benutzername oder Passwort sind falsch

Fehlermeldung: Authentication failed: wrong user/password

Weitere Anpassungen

Terms of Services aktivieren

Aktivieren Sie dazu das Feature termsAndPrivacyPolicy in der Datei config/jetstream.php

Dadurch sehen sie im Registrierungsdialog ein zusätzliches Optionsfeld

Ändern der Sprache

Bearbeiten Sie in der Datei config/app.php die Einstellung locale. Ändern Sie den Wert auf 'de'.

Nach einem Neustart der Anwendung werden aber weiterhin die englischen Texte angezeigt.

Die Ursache liegt an der nicht vorhandenen deutschen Übersetzung der verwendeten Texte. Prüfen Sie einfach den Ordner mit den vorhandenen Übersetzungen: resources/lang

Es gibt nur einen Unterordner en für die englischen Texte:

Zusätzlich zu den Übersetzungen muss noch ein weiterer Schritt erfolgen. Um ihre Anwendung komplett auf Mehrsprachlichkeit umzustellen dürfen keine Texte direkt angegeben werden.

Betrachten wir hierzu die Startseite, speziell die Links in der rechten oberen Ecke:

Der View für die Startseite wird in der Datei resources/views/welcome.blade.php definiert:

Wir sehen, das hier der Text direkt in englischer Sprache angegeben wird.

<a href="{{ route('register') }}" class="...">Register</a>

Es ist sehr schwierig, eine automatische Übersetzung für alle Texte durchzuführen, wenn nicht angegeben wird, ob ein bestimmter Text übersetzt werden soll. Einfach alle Texte zu übersetzen kann zu Problemen führen.

Daher wird bei Laravel der Weg gewählt, explizit anzugeben, ob ein Text übersetzt werden soll.

Diese Angabe der nachfolgende Text soll übersetzt werden erfolgt mit Hilfe der Funktion __ (Der Name der Funktion lautet tatsächlich __, als zwei Unterstriche)

Anstatt

Register

schreiben wir nun

{{ __('Register') }}

Dadurch erkennt Laravel (speziell die Blade Template Engine), das hier eine Übersetzung durchgeführt werden soll und sucht eine passenden Übersetzungstext.

Hier kommt nun der bereits angesprochen Ordner resources/lang ins Spiel. Erstellen Sie in diesem Ordner eine Datei de,json und verwenden Sie als Inhalt den nachfolgenden Text:

Nach einen Neustart der Anwendung sehen wir gewünschte Ergebnis:

Den gewünschten Übersetzungstext findet Blades anhand des angegebenen Textes innerhalb der Funktion __:

welcome.blade.phpde.json

Die Übersetzung der Anwendung erfordert somit für alle Texte die gleichen Schritte

  • Ermitteln des Views mit dem zu übersetzenden Text, z. B. Sample Text
  • Einbinden des Textes in die Funktion , z. B. {{ ('Sample text') }}
  • Hinzufügen der Übersetzung in der Datei de.json, z. B. "Sample text“: Beispieltext

Erstellen einer neue Startseite

Die Startseite entspricht immer noch der Standardanwendung. Der entsprechende View ist welcome.blade.php.

Zur Startseite wird dieser View aber nicht durch den Namen, sondern durch eine Einstellung in der Datei routes/web.php

Mit Hilfe des von Laravel verwendeten Routing, wird festgelegt, welcher View angezeigt wird, wenn eine bestimmte URI aufgerufen wird.

In der vorhandenen Einstellung legt fest, das bei der Uri '/' der View 'welcome' angezeigt wird.

Einfache Startseite erstellen

Wir verwenden hier das in der Laravel Dokumentation beschrieben einfache Beispiel.

Erstellen Sie die Datei Datei resources/views/home.blade.php. Verwenden Sie als Inhalt den nachfolgenden Text

<html>
    <body>
        <h1>Hello, {{ $name }}</h1>
    </body>
</html>

Routing anpassen

Passen Sie dann die Datei routes/web.php an.

Route::get('/welcome', function () {
    return view('welcome');
})->name('welcome');

Route::get('/', function () {
    return view('home', ['name' => 'Laravel']);
});

Wir ändern die neue Startseite mit der Uri / und stellen die alte Startseite unter einer neuen Uri /welcome bereit.

Neue Startseite anzeigen

Öffnen Sie nun wieder die Seite http://localhost:8000 oder aktualisieren Sie ein bereits geöffnetes Browserfenster:

Die vorherige Startseite können wir über die Uri /welcome erreichen

Neue Seite zur Navigation hinzufügen

Erweitern Sie die Daten resources/views/navigation-menu.blade.php um einen Eintrag:

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-jet-nav-link href="{{ route('dashboard') }}" 
                    :active="request()->routeIs('dashboard')">
         {{ __('Dashboard') }}
    </x-jet-nav-link>
</div>

<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-jet-nav-link href="{{ route('welcome') }}" 
                    :active="request()->routeIs('welcome')">
         {{ __('Welcome') }}
    </x-jet-nav-link>
</div>

Komponenten

Komponente erstellen

php artisan make:component MyComponent

Blade-Komponenten registrieren

You can register your jetstream blade components in App\Providers\JetstreamServiceProvider.php located in app\Providers folder.

Fügen sie die nachfolgende Funktion hinzu:

protected function registerComponent(string $component) {
    \Illuminate\Support\Facades\Blade::component('jetstream::components.'.$component, 'jet-'.$component);
}

Der nachfolgende Programmcode registriert ihre Komponente:

public function register() {
    $this->registerComponent('subform');
}

Sie können diese nun wie folgt nutzen:

<x-jet-subform>

Laravel | Einstieg in </> htmx

TL;DR

Der Code für die in diesem Post erstelle Anwendung liegt hier.

Einrichten der Starter-App

Erstellen der Starter App. In diesem Post beschreibe ich, wie sie eine Starter-App erstellen und die grundlegenden Anpassungen durchführen können.

Sie können auch direkt mit dem dazugehörigen Repository starten:

❯ git clone https://github.com/r14r/Laravel_Tutorial_Erstellen-einer-Starter-App Laravel_Tutorial_Arbeiten-mit-htmx
❯ composer install
❯ npm install
❯ npm run build
❯ php artisan
❯ cp .env.example  .env
❯ php artisan migrate
❯ php artisan key:generate
❯ php artisan serve

htmx einrichten

Installation

❯ npm install htmx.org

Hinweis: Verwenden sie nicht das NPM Paket htmx, dies hat einen anderen Inhalt

Seite für unsere Beipiele erstellen (htmx Playground)

Routing erstellen

Seite erstellen: resources/views/playground.blade.php

cp resources/views/dashboard.blade.php resources/views/playground.blade.php

Inhalt anpassen:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
            {{ __('Playground') }} for Laravel and <<span style="color:#3d72d7">/</span>> htm<span
                    style="color:#3d72d7">x</span>
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg">
                #
            </div>
        </div>
    </div>
</x-app-layout>

Navigationslinks anpassen

Ergebnis

Ersten htmx Aufruf hinzufügen

API Aufruf einrichten

Die Datei routes/api.php erweitern:

Route::get('/htmx/message', function () {
    return response('Dies ist eine Nachricht', 200)->header('Content-Type', 'application/json');
});

Playground Seite anpassen

<div hx-get="/api/htmx/message">
    Mitteilungen laden...
</div>

Ergebnis

htmx Beispiele

Auf der htmx – Projekteseite finden sie viele Beispiele. Wir wollen nun einige davon in Laravel umsetzen.

Laravel Toolbox

Laravel | Vergleich Inertia und Livewire Starter-kit

Vergleich der Dateien

TypeFileFolder
FilesKernel.phplaravel-starter-jetstream-inertia/app/Http
Filespackages.phplaravel-starter-jetstream-inertia/bootstrap/cache
Filesservices.phplaravel-starter-jetstream-inertia/bootstrap/cache
Filescomposer.jsonlaravel-starter-jetstream-inertia
Filesjetstream.phplaravel-starter-jetstream-inertia/config
Filespackage.jsonlaravel-starter-jetstream-inertia
Filesapp.csslaravel-starter-jetstream-inertia/public/css/app.css
Filesapp.jslaravel-starter-jetstream-inertia/public/js
Filesapp.jslaravel-starter-jetstream-inertia/resources/js
Filesweb.phplaravel-starter-jetstream-inertia/routes
Filestailwind.config.jslaravel-starter-jetstream-inertia
FilesApiTokenPermissionsTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesBrowserSessionsTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesCreateApiTokenTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesCreateTeamTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesDeleteAccountTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesDeleteApiTokenTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesDeleteTeamTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesInviteTeamMemberTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesLeaveTeamTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesProfileInformationTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesRemoveTeamMemberTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesTwoFactorAuthenticationSettingsTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesUpdatePasswordTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesUpdateTeamMemberRoleTest.phplaravel-starter-jetstream-inertia/tests/Feature
FilesUpdateTeamNameTest.phplaravel-starter-jetstream-inertia/tests/Feature
Filesautoload.phplaravel-starter-jetstream-inertia/vendor
Filesautoload_classmap.phplaravel-starter-jetstream-inertia/vendor/composer
Filesautoload_files.phplaravel-starter-jetstream-inertia/vendor/composer
Filesautoload_psr4.phplaravel-starter-jetstream-inertia/vendor/composer
Filesautoload_real.phplaravel-starter-jetstream-inertia/vendor/composer
Filesautoload_static.phplaravel-starter-jetstream-inertia/vendor/composer
Filesinstalled.jsonlaravel-starter-jetstream-inertia/vendor/composer
Filesinstalled.phplaravel-starter-jetstream-inertia/vendor/composer
Fileswebpack.mix.jslaravel-starter-jetstream-inertia
Only incomposer.locklaravel-starter-jetstream-inertia
Only inwebpack.config.jslaravel-starter-jetstream-inertia
Only inHandleInertiaRequests.phplaravel-starter-jetstream-inertia/app/Http/Middleware
Only in2022_01_16_142014_create_sessions_table.phplaravel-starter-jetstream-inertia/database/migrations
Only inJetstreamlaravel-starter-jetstream-inertia/resources/js
Only inLayoutslaravel-starter-jetstream-inertia/resources/js
Only inPageslaravel-starter-jetstream-inertia/resources/js
Only inapp.blade.phplaravel-starter-jetstream-inertia/resources/views
Only inlaravel.loglaravel-starter-jetstream-inertia/storage/logs
Only ininertiajslaravel-starter-jetstream-inertia/vendor
Only intightencolaravel-starter-jetstream-inertia/vendor
Only inViewlaravel-starter-jetstream-livewire/app
Only in2022_01_16_142135_create_sessions_table.phplaravel-starter-jetstream-livewire/database/migrations
Only inapilaravel-starter-jetstream-livewire/resources/views
Only inauthlaravel-starter-jetstream-livewire/resources/views
Only indashboard.blade.phplaravel-starter-jetstream-livewire/resources/views
Only inlayoutslaravel-starter-jetstream-livewire/resources/views
Only innavigation-menu.blade.phplaravel-starter-jetstream-livewire/resources/views
Only inpolicy.blade.phplaravel-starter-jetstream-livewire/resources/views
Only inprofilelaravel-starter-jetstream-livewire/resources/views
Only inteamslaravel-starter-jetstream-livewire/resources/views
Only interms.blade.phplaravel-starter-jetstream-livewire/resources/views
Only inwelcome.blade.phplaravel-starter-jetstream-livewire/resources/views
Only inlivewirelaravel-starter-jetstream-livewire/vendor

Änderungen

app/Http/Kernel.php

Changes

<             \App\Http\Middleware\HandleInertiaRequests::class,

Only inapp/Http/Middleware: HandleInertiaRequests.php

Only inlaravel-starter-jetstream-livewire/app: View

bootstrap/cache/packages.php

Changes

<   'inertiajs/inertia-laravel' => 
<   array (
<     'providers' => 
<     array (
<       0 => 'Inertia\\ServiceProvider',
<     ),
<   ),

Changes

<   'nesbot/carbon' => 
---
>   'livewire/livewire' =>

Changes

<       0 => 'Carbon\\Laravel\\ServiceProvider',
---
>       0 => 'Livewire\\LivewireServiceProvider',
>     ),
>     'aliases' => 
>     array (
>       'Livewire' => 'Livewire\\Livewire',

Changes

<   'nunomaduro/collision' => 
---
>   'nesbot/carbon' =>

Changes

<       0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
---
>       0 => 'Carbon\\Laravel\\ServiceProvider',

Changes

<   'tightenco/ziggy' => 
---
>   'nunomaduro/collision' =>

Changes

<       0 => 'Tightenco\\Ziggy\\ZiggyServiceProvider',
---
>       0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',

bootstrap/cache/services.php

Changes

<     24 => 'Inertia\\ServiceProvider',
<     25 => 'Jenssegers\\Agent\\AgentServiceProvider',
<     26 => 'Laravel\\Fortify\\FortifyServiceProvider',
<     27 => 'Laravel\\Jetstream\\JetstreamServiceProvider',
<     28 => 'Laravel\\Sail\\SailServiceProvider',
<     29 => 'Laravel\\Sanctum\\SanctumServiceProvider',
<     30 => 'Laravel\\Tinker\\TinkerServiceProvider',
---
>     24 => 'Jenssegers\\Agent\\AgentServiceProvider',
>     25 => 'Laravel\\Fortify\\FortifyServiceProvider',
>     26 => 'Laravel\\Jetstream\\JetstreamServiceProvider',
>     27 => 'Laravel\\Sail\\SailServiceProvider',
>     28 => 'Laravel\\Sanctum\\SanctumServiceProvider',
>     29 => 'Laravel\\Tinker\\TinkerServiceProvider',
>     30 => 'Livewire\\LivewireServiceProvider',

Changes

<     33 => 'Tightenco\\Ziggy\\ZiggyServiceProvider',
<     34 => 'App\\Providers\\AppServiceProvider',
<     35 => 'App\\Providers\\AuthServiceProvider',
<     36 => 'App\\Providers\\EventServiceProvider',
<     37 => 'App\\Providers\\RouteServiceProvider',
<     38 => 'App\\Providers\\FortifyServiceProvider',
<     39 => 'App\\Providers\\JetstreamServiceProvider',
---
>     33 => 'App\\Providers\\AppServiceProvider',
>     34 => 'App\\Providers\\AuthServiceProvider',
>     35 => 'App\\Providers\\EventServiceProvider',
>     36 => 'App\\Providers\\RouteServiceProvider',
>     37 => 'App\\Providers\\FortifyServiceProvider',
>     38 => 'App\\Providers\\JetstreamServiceProvider',

Changes

<     12 => 'Inertia\\ServiceProvider',
<     13 => 'Jenssegers\\Agent\\AgentServiceProvider',
<     14 => 'Laravel\\Fortify\\FortifyServiceProvider',
<     15 => 'Laravel\\Jetstream\\JetstreamServiceProvider',
<     16 => 'Laravel\\Sanctum\\SanctumServiceProvider',
---
>     12 => 'Jenssegers\\Agent\\AgentServiceProvider',
>     13 => 'Laravel\\Fortify\\FortifyServiceProvider',
>     14 => 'Laravel\\Jetstream\\JetstreamServiceProvider',
>     15 => 'Laravel\\Sanctum\\SanctumServiceProvider',
>     16 => 'Livewire\\LivewireServiceProvider',

Changes

<     19 => 'Tightenco\\Ziggy\\ZiggyServiceProvider',
<     20 => 'App\\Providers\\AppServiceProvider',
<     21 => 'App\\Providers\\AuthServiceProvider',
<     22 => 'App\\Providers\\EventServiceProvider',
<     23 => 'App\\Providers\\RouteServiceProvider',
<     24 => 'App\\Providers\\FortifyServiceProvider',
<     25 => 'App\\Providers\\JetstreamServiceProvider',
---
>     19 => 'App\\Providers\\AppServiceProvider',
>     20 => 'App\\Providers\\AuthServiceProvider',
>     21 => 'App\\Providers\\EventServiceProvider',
>     22 => 'App\\Providers\\RouteServiceProvider',
>     23 => 'App\\Providers\\FortifyServiceProvider',
>     24 => 'App\\Providers\\JetstreamServiceProvider',

composer.json

Changes

<         "inertiajs/inertia-laravel": "^0.5.2",

Changes

<         "tightenco/ziggy": "^1.0"
---
>         "livewire/livewire": "^2.5"

Only in laravel-starter-jetstream-inertia: composer.lock

config/jetstream.php

Changes

<     'stack' => 'inertia',
---
>     'stack' => 'livewire',

Only in database/migrations: 2022_01_16_142014_create_sessions_table.php

Only in

package.json

Changes

<         "@inertiajs/inertia": "^0.10.0",
<         "@inertiajs/inertia-vue3": "^0.5.1",
<         "@inertiajs/progress": "^0.2.6",

Changes

<         "@vue/compiler-sfc": "^3.0.5",
---
>         "alpinejs": "^3.0.6",

Changes

<         "postcss-import": "^12.0.1",
---
>         "postcss-import": "^14.0.1",

Changes

<         "vue": "^3.0.5",
<         "vue-loader": "^16.1.2",

Only in laravel-starter-jetstream-livewire: pnpm-lock.yaml

Only in resources/js: Jetstream

Only in resources/js: Layouts

Only in resources/js: Pages

resources/js/app.js

Changes

< import { createApp, h } from 'vue';
< import { createInertiaApp } from '@inertiajs/inertia-vue3';
< import { InertiaProgress } from '@inertiajs/progress';
---
> import Alpine from 'alpinejs';

Changes

< const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
---
> window.Alpine = Alpine;

Changes

< createInertiaApp({
<     title: (title) => `${title} - ${appName}`,
<     resolve: (name) => require(`./Pages/${name}.vue`),
<     setup({ el, app, props, plugin }) {
<         return createApp({ render: () => h(app, props) })
<             .use(plugin)
<             .mixin({ methods: { route } })
<             .mount(el);
<     },
< });
< 
< InertiaProgress.init({ color: '#4B5563' });
---
> Alpine.start();

Only in

Only in resources/views: app.blade.php

Only in

Only in

Only in

Only inlaravel-starter-jetstream-livewire/resources/views: navigation-menu.blade.php

Only inlaravel-starter-jetstream-livewire/resources/views: policy.blade.php

Only inlaravel-starter-jetstream-livewire/resources/views: profile

Only inlaravel-starter-jetstream-livewire/resources/views: teams

Only inlaravel-starter-jetstream-livewire/resources/views: terms.blade.php

Only inlaravel-starter-jetstream-livewire/resources/views: welcome.blade.php

routes/web.php

Changes

< use Illuminate\Foundation\Application;

Changes

< use Inertia\Inertia;

Changes

<     return Inertia::render('Welcome', [
<         'canLogin' => Route::has('login'),
<         'canRegister' => Route::has('register'),
<         'laravelVersion' => Application::VERSION,
<         'phpVersion' => PHP_VERSION,
<     ]);
---
>     return view('welcome');

Changes

<     return Inertia::render('Dashboard');
---
>     return view('dashboard');

Only instorage/logs: laravel.log

tailwind.config.js

Changes

<         './resources/js/**/*.vue',

tests/Feature/ApiTokenPermissionsTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
> use Livewire\Livewire;

Changes

<         $response = $this->put('/user/api-tokens/'.$token->id, [
<             'name' => $token->name,
<             'permissions' => [
<                 'delete',
<                 'missing-permission',
<             ],
<         ]);
---
>         Livewire::test(ApiTokenManager::class)
>                     ->set(['managingPermissionsFor' => $token])
>                     ->set(['updateApiTokenForm' => [
>                         'permissions' => [
>                             'delete',
>                             'missing-permission',
>                         ],
>                     ]])
>                     ->call('updateApiToken');
## tests/Feature/BrowserSessionsTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/user/other-browser-sessions', [
<             'password' => 'password',
<         ]);
< 
<         $response->assertSessionHasNoErrors();
---
>         Livewire::test(LogoutOtherBrowserSessionsForm::class)
>                 ->set('password', 'password')
>                 ->call('logoutOtherBrowserSessions');
## tests/Feature/CreateApiTokenTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
> use Livewire\Livewire;

Changes

<         $response = $this->post('/user/api-tokens', [
<             'name' => 'Test Token',
<             'permissions' => [
<                 'read',
<                 'update',
<             ],
<         ]);
---
>         Livewire::test(ApiTokenManager::class)
>                     ->set(['createApiTokenForm' => [
>                         'name' => 'Test Token',
>                         'permissions' => [
>                             'read',
>                             'update',
>                         ],
>                     ]])
>                     ->call('createApiToken');
## tests/Feature/CreateTeamTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\CreateTeamForm;
> use Livewire\Livewire;

Changes

<         $response = $this->post('/teams', [
<             'name' => 'Test Team',
<         ]);
---
>         Livewire::test(CreateTeamForm::class)
>                     ->set(['state' => ['name' => 'Test Team']])
>                     ->call('createTeam');
## tests/Feature/DeleteAccountTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\DeleteUserForm;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/user', [
<             'password' => 'password',
<         ]);
---
>         $component = Livewire::test(DeleteUserForm::class)
>                         ->set('password', 'password')
>                         ->call('deleteUser');

Changes

<         $response = $this->delete('/user', [
<             'password' => 'wrong-password',
<         ]);
---
>         Livewire::test(DeleteUserForm::class)
>                         ->set('password', 'wrong-password')
>                         ->call('deleteUser')
>                         ->assertHasErrors(['password']);
## tests/Feature/DeleteApiTokenTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/user/api-tokens/'.$token->id);
---
>         Livewire::test(ApiTokenManager::class)
>                     ->set(['apiTokenIdBeingDeleted' => $token->id])
>                     ->call('deleteApiToken');
## tests/Feature/DeleteTeamTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\DeleteTeamForm;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/teams/'.$team->id);
---
>         $component = Livewire::test(DeleteTeamForm::class, ['team' => $team->fresh()])
>                                 ->call('deleteTeam');

Changes

<         $response = $this->delete('/teams/'.$user->currentTeam->id);
---
>         $component = Livewire::test(DeleteTeamForm::class, ['team' => $user->currentTeam])
>                                 ->call('deleteTeam')
>                                 ->assertHasErrors(['team']);
## tests/Feature/InviteTeamMemberTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\TeamMemberManager;

Changes

> use Livewire\Livewire;

Changes

<         $response = $this->post('/teams/'.$user->currentTeam->id.'/members', [
<             'email' => 'test@example.com',
<             'role' => 'admin',
<         ]);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('addTeamMemberForm', [
>                             'email' => 'test@example.com',
>                             'role' => 'admin',
>                         ])->call('addTeamMember');

Changes

<         $invitation = $user->currentTeam->teamInvitations()->create([
<             'email' => 'test@example.com',
<             'role' => 'admin',
<         ]);
---
>         // Add the team member...
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('addTeamMemberForm', [
>                             'email' => 'test@example.com',
>                             'role' => 'admin',
>                         ])->call('addTeamMember');

Changes

<         $response = $this->delete('/team-invitations/'.$invitation->id);
---
>         $invitationId = $user->currentTeam->fresh()->teamInvitations->first()->id;
> 
>         // Cancel the team invitation...
>         $component->call('cancelTeamInvitation', $invitationId);

tests/Feature/LeaveTeamTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\TeamMemberManager;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/teams/'.$user->currentTeam->id.'/members/'.$otherUser->id);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->call('leaveTeam');

Changes

<         $response = $this->delete('/teams/'.$user->currentTeam->id.'/members/'.$user->id);
< 
<         $response->assertSessionHasErrorsIn('removeTeamMember', ['team']);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->call('leaveTeam')
>                         ->assertHasErrors(['team']);
## tests/Feature/ProfileInformationTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\UpdateProfileInformationForm;
> use Livewire\Livewire;

Changes

>     public function test_current_profile_information_is_available()
>     {
>         $this->actingAs($user = User::factory()->create());
> 
>         $component = Livewire::test(UpdateProfileInformationForm::class);
> 
>         $this->assertEquals($user->name, $component->state['name']);
>         $this->assertEquals($user->email, $component->state['email']);
>     }
> 

Changes

<         $response = $this->put('/user/profile-information', [
<             'name' => 'Test Name',
<             'email' => 'test@example.com',
<         ]);
---
>         Livewire::test(UpdateProfileInformationForm::class)
>                 ->set('state', ['name' => 'Test Name', 'email' => 'test@example.com'])
>                 ->call('updateProfileInformation');
## tests/Feature/RemoveTeamMemberTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\TeamMemberManager;
> use Livewire\Livewire;

Changes

<         $response = $this->delete('/teams/'.$user->currentTeam->id.'/members/'.$otherUser->id);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('teamMemberIdBeingRemoved', $otherUser->id)
>                         ->call('removeTeamMember');

Changes

<         $response = $this->delete('/teams/'.$user->currentTeam->id.'/members/'.$user->id);
< 
<         $response->assertStatus(403);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('teamMemberIdBeingRemoved', $user->id)
>                         ->call('removeTeamMember')
>                         ->assertStatus(403);
## tests/Feature/TwoFactorAuthenticationSettingsTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\TwoFactorAuthenticationForm;
> use Livewire\Livewire;

Changes

<         $response = $this->post('/user/two-factor-authentication');
---
>         Livewire::test(TwoFactorAuthenticationForm::class)
>                 ->call('enableTwoFactorAuthentication');

Changes

<         $this->assertNotNull($user->fresh()->two_factor_secret);
<         $this->assertCount(8, $user->fresh()->recoveryCodes());
---
>         $user = $user->fresh();
> 
>         $this->assertNotNull($user->two_factor_secret);
>         $this->assertCount(8, $user->recoveryCodes());

Changes

<         $this->post('/user/two-factor-authentication');
<         $this->post('/user/two-factor-recovery-codes');
---
>         $component = Livewire::test(TwoFactorAuthenticationForm::class)
>                 ->call('enableTwoFactorAuthentication')
>                 ->call('regenerateRecoveryCodes');

Changes

<         $this->post('/user/two-factor-recovery-codes');
---
>         $component->call('regenerateRecoveryCodes');

Changes

<         $this->post('/user/two-factor-authentication');
---
>         $component = Livewire::test(TwoFactorAuthenticationForm::class)
>                 ->call('enableTwoFactorAuthentication');

Changes

<         $this->delete('/user/two-factor-authentication');
---
>         $component->call('disableTwoFactorAuthentication');
## tests/Feature/UpdatePasswordTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\UpdatePasswordForm;
> use Livewire\Livewire;

Changes

<         $response = $this->put('/user/password', [
<             'current_password' => 'password',
<             'password' => 'new-password',
<             'password_confirmation' => 'new-password',
<         ]);
---
>         Livewire::test(UpdatePasswordForm::class)
>                 ->set('state', [
>                     'current_password' => 'password',
>                     'password' => 'new-password',
>                     'password_confirmation' => 'new-password',
>                 ])
>                 ->call('updatePassword');

Changes

<         $response = $this->put('/user/password', [
<             'current_password' => 'wrong-password',
<             'password' => 'new-password',
<             'password_confirmation' => 'new-password',
<         ]);
< 
<         $response->assertSessionHasErrors();
---
>         Livewire::test(UpdatePasswordForm::class)
>                 ->set('state', [
>                     'current_password' => 'wrong-password',
>                     'password' => 'new-password',
>                     'password_confirmation' => 'new-password',
>                 ])
>                 ->call('updatePassword')
>                 ->assertHasErrors(['current_password']);

Changes

<         $response = $this->put('/user/password', [
<             'current_password' => 'password',
<             'password' => 'new-password',
<             'password_confirmation' => 'wrong-password',
<         ]);
< 
<         $response->assertSessionHasErrors();
---
>         Livewire::test(UpdatePasswordForm::class)
>                 ->set('state', [
>                     'current_password' => 'password',
>                     'password' => 'new-password',
>                     'password_confirmation' => 'wrong-password',
>                 ])
>                 ->call('updatePassword')
>                 ->assertHasErrors(['password']);
## tests/Feature/UpdateTeamMemberRoleTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\TeamMemberManager;
> use Livewire\Livewire;

Changes

<         $response = $this->put('/teams/'.$user->currentTeam->id.'/members/'.$otherUser->id, [
<             'role' => 'editor',
<         ]);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('managingRoleFor', $otherUser)
>                         ->set('currentRole', 'editor')
>                         ->call('updateRole');

Changes

<         $response = $this->put('/teams/'.$user->currentTeam->id.'/members/'.$otherUser->id, [
<             'role' => 'editor',
<         ]);
---
>         $component = Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam])
>                         ->set('managingRoleFor', $otherUser)
>                         ->set('currentRole', 'editor')
>                         ->call('updateRole')
>                         ->assertStatus(403);
## tests/Feature/UpdateTeamNameTest.php

Changes

> use Laravel\Jetstream\Http\Livewire\UpdateTeamNameForm;
> use Livewire\Livewire;

Changes

<         $response = $this->put('/teams/'.$user->currentTeam->id, [
<             'name' => 'Test Team',
<         ]);
---
>         Livewire::test(UpdateTeamNameForm::class, ['team' => $user->currentTeam])
>                     ->set(['state' => ['name' => 'Test Team']])
>                     ->call('updateTeamName');

vendor/autoload.php

Changes

< return ComposerAutoloaderInit1a366ea8c9013f37377c59393191f427::getLoader();
---
> return ComposerAutoloaderInitf2da6e83b085fd96649a1eced6ba9cbb::getLoader();
## vendor/composer/autoload_classmap.php

Changes

<     'Inertia\\Console\\CreateMiddleware' => $vendorDir . '/inertiajs/inertia-laravel/src/Console/CreateMiddleware.php',
<     'Inertia\\Controller' => $vendorDir . '/inertiajs/inertia-laravel/src/Controller.php',
<     'Inertia\\Directive' => $vendorDir . '/inertiajs/inertia-laravel/src/Directive.php',
<     'Inertia\\Inertia' => $vendorDir . '/inertiajs/inertia-laravel/src/Inertia.php',
<     'Inertia\\LazyProp' => $vendorDir . '/inertiajs/inertia-laravel/src/LazyProp.php',
<     'Inertia\\Middleware' => $vendorDir . '/inertiajs/inertia-laravel/src/Middleware.php',
<     'Inertia\\Response' => $vendorDir . '/inertiajs/inertia-laravel/src/Response.php',
<     'Inertia\\ResponseFactory' => $vendorDir . '/inertiajs/inertia-laravel/src/ResponseFactory.php',
<     'Inertia\\ServiceProvider' => $vendorDir . '/inertiajs/inertia-laravel/src/ServiceProvider.php',
<     'Inertia\\Ssr\\Gateway' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/Gateway.php',
<     'Inertia\\Ssr\\HttpGateway' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/HttpGateway.php',
<     'Inertia\\Ssr\\Response' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/Response.php',
<     'Inertia\\Testing\\Assert' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Assert.php',
<     'Inertia\\Testing\\AssertableInertia' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/AssertableInertia.php',
<     'Inertia\\Testing\\Concerns\\Debugging' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Debugging.php',
<     'Inertia\\Testing\\Concerns\\Has' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Has.php',
<     'Inertia\\Testing\\Concerns\\Interaction' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Interaction.php',
<     'Inertia\\Testing\\Concerns\\Matching' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Matching.php',
<     'Inertia\\Testing\\Concerns\\PageObject' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/PageObject.php',
<     'Inertia\\Testing\\TestResponseMacros' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/TestResponseMacros.php',

Changes

>     'Livewire\\Castable' => $vendorDir . '/livewire/livewire/src/Castable.php',
>     'Livewire\\Commands\\ComponentParser' => $vendorDir . '/livewire/livewire/src/Commands/ComponentParser.php',
>     'Livewire\\Commands\\ComponentParserFromExistingComponent' => $vendorDir . '/livewire/livewire/src/Commands/ComponentParserFromExistingComponent.php',
>     'Livewire\\Commands\\CopyCommand' => $vendorDir . '/livewire/livewire/src/Commands/CopyCommand.php',
>     'Livewire\\Commands\\CpCommand' => $vendorDir . '/livewire/livewire/src/Commands/CpCommand.php',
>     'Livewire\\Commands\\DeleteCommand' => $vendorDir . '/livewire/livewire/src/Commands/DeleteCommand.php',
>     'Livewire\\Commands\\DiscoverCommand' => $vendorDir . '/livewire/livewire/src/Commands/DiscoverCommand.php',
>     'Livewire\\Commands\\FileManipulationCommand' => $vendorDir . '/livewire/livewire/src/Commands/FileManipulationCommand.php',
>     'Livewire\\Commands\\MakeCommand' => $vendorDir . '/livewire/livewire/src/Commands/MakeCommand.php',
>     'Livewire\\Commands\\MakeLivewireCommand' => $vendorDir . '/livewire/livewire/src/Commands/MakeLivewireCommand.php',
>     'Livewire\\Commands\\MoveCommand' => $vendorDir . '/livewire/livewire/src/Commands/MoveCommand.php',
>     'Livewire\\Commands\\MvCommand' => $vendorDir . '/livewire/livewire/src/Commands/MvCommand.php',
>     'Livewire\\Commands\\PublishCommand' => $vendorDir . '/livewire/livewire/src/Commands/PublishCommand.php',
>     'Livewire\\Commands\\RmCommand' => $vendorDir . '/livewire/livewire/src/Commands/RmCommand.php',
>     'Livewire\\Commands\\S3CleanupCommand' => $vendorDir . '/livewire/livewire/src/Commands/S3CleanupCommand.php',
>     'Livewire\\Commands\\StubParser' => $vendorDir . '/livewire/livewire/src/Commands/StubParser.php',
>     'Livewire\\Commands\\StubsCommand' => $vendorDir . '/livewire/livewire/src/Commands/StubsCommand.php',
>     'Livewire\\Commands\\TouchCommand' => $vendorDir . '/livewire/livewire/src/Commands/TouchCommand.php',
>     'Livewire\\CompilerEngineForIgnition' => $vendorDir . '/livewire/livewire/src/CompilerEngineForIgnition.php',
>     'Livewire\\Component' => $vendorDir . '/livewire/livewire/src/Component.php',
>     'Livewire\\ComponentChecksumManager' => $vendorDir . '/livewire/livewire/src/ComponentChecksumManager.php',
>     'Livewire\\ComponentConcerns\\HandlesActions' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/HandlesActions.php',
>     'Livewire\\ComponentConcerns\\InteractsWithProperties' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/InteractsWithProperties.php',
>     'Livewire\\ComponentConcerns\\PerformsRedirects' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/PerformsRedirects.php',
>     'Livewire\\ComponentConcerns\\ReceivesEvents' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/ReceivesEvents.php',
>     'Livewire\\ComponentConcerns\\RendersLivewireComponents' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/RendersLivewireComponents.php',
>     'Livewire\\ComponentConcerns\\TracksRenderedChildren' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/TracksRenderedChildren.php',
>     'Livewire\\ComponentConcerns\\ValidatesInput' => $vendorDir . '/livewire/livewire/src/ComponentConcerns/ValidatesInput.php',
>     'Livewire\\Connection\\ConnectionHandler' => $vendorDir . '/livewire/livewire/src/Connection/ConnectionHandler.php',
>     'Livewire\\Controllers\\CanPretendToBeAFile' => $vendorDir . '/livewire/livewire/src/Controllers/CanPretendToBeAFile.php',
>     'Livewire\\Controllers\\FilePreviewHandler' => $vendorDir . '/livewire/livewire/src/Controllers/FilePreviewHandler.php',
>     'Livewire\\Controllers\\FileUploadHandler' => $vendorDir . '/livewire/livewire/src/Controllers/FileUploadHandler.php',
>     'Livewire\\Controllers\\HttpConnectionHandler' => $vendorDir . '/livewire/livewire/src/Controllers/HttpConnectionHandler.php',
>     'Livewire\\Controllers\\LivewireJavaScriptAssets' => $vendorDir . '/livewire/livewire/src/Controllers/LivewireJavaScriptAssets.php',
>     'Livewire\\CreateBladeView' => $vendorDir . '/livewire/livewire/src/CreateBladeView.php',
>     'Livewire\\DisableBrowserCache' => $vendorDir . '/livewire/livewire/src/DisableBrowserCache.php',
>     'Livewire\\Event' => $vendorDir . '/livewire/livewire/src/Event.php',
>     'Livewire\\Exceptions\\BypassViewHandler' => $vendorDir . '/livewire/livewire/src/Exceptions/BypassViewHandler.php',
>     'Livewire\\Exceptions\\CannotBindToModelDataWithoutValidationRuleException' => $vendorDir . '/livewire/livewire/src/Exceptions/CannotBindToModelDataWithoutValidationRuleException.php',
>     'Livewire\\Exceptions\\CannotUseReservedLivewireComponentProperties' => $vendorDir . '/livewire/livewire/src/Exceptions/CannotUseReservedLivewireComponentProperties.php',
>     'Livewire\\Exceptions\\ComponentAttributeMissingOnDynamicComponentException' => $vendorDir . '/livewire/livewire/src/Exceptions/ComponentAttributeMissingOnDynamicComponentException.php',
>     'Livewire\\Exceptions\\ComponentNotFoundException' => $vendorDir . '/livewire/livewire/src/Exceptions/ComponentNotFoundException.php',
>     'Livewire\\Exceptions\\CorruptComponentPayloadException' => $vendorDir . '/livewire/livewire/src/Exceptions/CorruptComponentPayloadException.php',
>     'Livewire\\Exceptions\\DirectlyCallingLifecycleHooksNotAllowedException' => $vendorDir . '/livewire/livewire/src/Exceptions/DirectlyCallingLifecycleHooksNotAllowedException.php',
>     'Livewire\\Exceptions\\LivewirePageExpiredBecauseNewDeploymentHasSignificantEnoughChanges' => $vendorDir . '/livewire/livewire/src/Exceptions/LivewirePageExpiredBecauseNewDeploymentHasSignificantEnoughChanges.php',
>     'Livewire\\Exceptions\\MethodNotFoundException' => $vendorDir . '/livewire/livewire/src/Exceptions/MethodNotFoundException.php',
>     'Livewire\\Exceptions\\MissingFileUploadsTraitException' => $vendorDir . '/livewire/livewire/src/Exceptions/MissingFileUploadsTraitException.php',
>     'Livewire\\Exceptions\\MissingRulesException' => $vendorDir . '/livewire/livewire/src/Exceptions/MissingRulesException.php',
>     'Livewire\\Exceptions\\NonPublicComponentMethodCall' => $vendorDir . '/livewire/livewire/src/Exceptions/NonPublicComponentMethodCall.php',
>     'Livewire\\Exceptions\\PropertyNotFoundException' => $vendorDir . '/livewire/livewire/src/Exceptions/PropertyNotFoundException.php',
>     'Livewire\\Exceptions\\PublicPropertyNotFoundException' => $vendorDir . '/livewire/livewire/src/Exceptions/PublicPropertyNotFoundException.php',
>     'Livewire\\Exceptions\\PublicPropertyTypeNotAllowedException' => $vendorDir . '/livewire/livewire/src/Exceptions/PublicPropertyTypeNotAllowedException.php',
>     'Livewire\\Exceptions\\RootTagMissingFromViewException' => $vendorDir . '/livewire/livewire/src/Exceptions/RootTagMissingFromViewException.php',
>     'Livewire\\Exceptions\\S3DoesntSupportMultipleFileUploads' => $vendorDir . '/livewire/livewire/src/Exceptions/S3DoesntSupportMultipleFileUploads.php',
>     'Livewire\\Features\\OptimizeRenderedDom' => $vendorDir . '/livewire/livewire/src/Features/OptimizeRenderedDom.php',
>     'Livewire\\Features\\Placeholder' => $vendorDir . '/livewire/livewire/src/Features/Placeholder.php',
>     'Livewire\\Features\\SupportActionReturns' => $vendorDir . '/livewire/livewire/src/Features/SupportActionReturns.php',
>     'Livewire\\Features\\SupportBootMethod' => $vendorDir . '/livewire/livewire/src/Features/SupportBootMethod.php',
>     'Livewire\\Features\\SupportBrowserHistory' => $vendorDir . '/livewire/livewire/src/Features/SupportBrowserHistory.php',
>     'Livewire\\Features\\SupportChildren' => $vendorDir . '/livewire/livewire/src/Features/SupportChildren.php',
>     'Livewire\\Features\\SupportCollections' => $vendorDir . '/livewire/livewire/src/Features/SupportCollections.php',
>     'Livewire\\Features\\SupportComponentTraits' => $vendorDir . '/livewire/livewire/src/Features/SupportComponentTraits.php',
>     'Livewire\\Features\\SupportDateTimes' => $vendorDir . '/livewire/livewire/src/Features/SupportDateTimes.php',
>     'Livewire\\Features\\SupportEvents' => $vendorDir . '/livewire/livewire/src/Features/SupportEvents.php',
>     'Livewire\\Features\\SupportFileDownloads' => $vendorDir . '/livewire/livewire/src/Features/SupportFileDownloads.php',
>     'Livewire\\Features\\SupportFileUploads' => $vendorDir . '/livewire/livewire/src/Features/SupportFileUploads.php',
>     'Livewire\\Features\\SupportLocales' => $vendorDir . '/livewire/livewire/src/Features/SupportLocales.php',
>     'Livewire\\Features\\SupportPostDeploymentInvalidation' => $vendorDir . '/livewire/livewire/src/Features/SupportPostDeploymentInvalidation.php',
>     'Livewire\\Features\\SupportRedirects' => $vendorDir . '/livewire/livewire/src/Features/SupportRedirects.php',
>     'Livewire\\Features\\SupportRootElementTracking' => $vendorDir . '/livewire/livewire/src/Features/SupportRootElementTracking.php',
>     'Livewire\\Features\\SupportStacks' => $vendorDir . '/livewire/livewire/src/Features/SupportStacks.php',
>     'Livewire\\Features\\SupportValidation' => $vendorDir . '/livewire/livewire/src/Features/SupportValidation.php',
>     'Livewire\\FileUploadConfiguration' => $vendorDir . '/livewire/livewire/src/FileUploadConfiguration.php',
>     'Livewire\\GenerateSignedUploadUrl' => $vendorDir . '/livewire/livewire/src/GenerateSignedUploadUrl.php',
>     'Livewire\\HydrationMiddleware\\AddAttributesToRootTagOfHtml' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/AddAttributesToRootTagOfHtml.php',
>     'Livewire\\HydrationMiddleware\\CallHydrationHooks' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/CallHydrationHooks.php',
>     'Livewire\\HydrationMiddleware\\CallPropertyHydrationHooks' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/CallPropertyHydrationHooks.php',
>     'Livewire\\HydrationMiddleware\\HashDataPropertiesForDirtyDetection' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/HashDataPropertiesForDirtyDetection.php',
>     'Livewire\\HydrationMiddleware\\HydratePublicProperties' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php',
>     'Livewire\\HydrationMiddleware\\HydrationMiddleware' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/HydrationMiddleware.php',
>     'Livewire\\HydrationMiddleware\\NormalizeComponentPropertiesForJavaScript' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/NormalizeComponentPropertiesForJavaScript.php',
>     'Livewire\\HydrationMiddleware\\NormalizeDataForJavaScript' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/NormalizeDataForJavaScript.php',
>     'Livewire\\HydrationMiddleware\\NormalizeServerMemoSansDataForJavaScript' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/NormalizeServerMemoSansDataForJavaScript.php',
>     'Livewire\\HydrationMiddleware\\PerformActionCalls' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/PerformActionCalls.php',
>     'Livewire\\HydrationMiddleware\\PerformDataBindingUpdates' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/PerformDataBindingUpdates.php',
>     'Livewire\\HydrationMiddleware\\PerformEventEmissions' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/PerformEventEmissions.php',
>     'Livewire\\HydrationMiddleware\\RenderView' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/RenderView.php',
>     'Livewire\\HydrationMiddleware\\SecureHydrationWithChecksum' => $vendorDir . '/livewire/livewire/src/HydrationMiddleware/SecureHydrationWithChecksum.php',
>     'Livewire\\ImplicitRouteBinding' => $vendorDir . '/livewire/livewire/src/ImplicitRouteBinding.php',
>     'Livewire\\ImplicitlyBoundMethod' => $vendorDir . '/livewire/livewire/src/ImplicitlyBoundMethod.php',
>     'Livewire\\LifecycleManager' => $vendorDir . '/livewire/livewire/src/LifecycleManager.php',
>     'Livewire\\Livewire' => $vendorDir . '/livewire/livewire/src/Livewire.php',
>     'Livewire\\LivewireBladeDirectives' => $vendorDir . '/livewire/livewire/src/LivewireBladeDirectives.php',
>     'Livewire\\LivewireComponentsFinder' => $vendorDir . '/livewire/livewire/src/LivewireComponentsFinder.php',
>     'Livewire\\LivewireManager' => $vendorDir . '/livewire/livewire/src/LivewireManager.php',
>     'Livewire\\LivewireServiceProvider' => $vendorDir . '/livewire/livewire/src/LivewireServiceProvider.php',
>     'Livewire\\LivewireTagCompiler' => $vendorDir . '/livewire/livewire/src/LivewireTagCompiler.php',
>     'Livewire\\LivewireViewCompilerEngine' => $vendorDir . '/livewire/livewire/src/LivewireViewCompilerEngine.php',
>     'Livewire\\Macros\\DuskBrowserMacros' => $vendorDir . '/livewire/livewire/src/Macros/DuskBrowserMacros.php',
>     'Livewire\\Macros\\ViewMacros' => $vendorDir . '/livewire/livewire/src/Macros/ViewMacros.php',
>     'Livewire\\ObjectPrybar' => $vendorDir . '/livewire/livewire/src/ObjectPrybar.php',
>     'Livewire\\Redirector' => $vendorDir . '/livewire/livewire/src/Redirector.php',
>     'Livewire\\Request' => $vendorDir . '/livewire/livewire/src/Request.php',
>     'Livewire\\Response' => $vendorDir . '/livewire/livewire/src/Response.php',
>     'Livewire\\TemporaryUploadedFile' => $vendorDir . '/livewire/livewire/src/TemporaryUploadedFile.php',
>     'Livewire\\Testing\\Concerns\\HasFunLittleUtilities' => $vendorDir . '/livewire/livewire/src/Testing/Concerns/HasFunLittleUtilities.php',
>     'Livewire\\Testing\\Concerns\\MakesAssertions' => $vendorDir . '/livewire/livewire/src/Testing/Concerns/MakesAssertions.php',
>     'Livewire\\Testing\\Concerns\\MakesCallsToComponent' => $vendorDir . '/livewire/livewire/src/Testing/Concerns/MakesCallsToComponent.php',
>     'Livewire\\Testing\\MakesHttpRequestsWrapper' => $vendorDir . '/livewire/livewire/src/Testing/MakesHttpRequestsWrapper.php',
>     'Livewire\\Testing\\TestableLivewire' => $vendorDir . '/livewire/livewire/src/Testing/TestableLivewire.php',
>     'Livewire\\WireDirective' => $vendorDir . '/livewire/livewire/src/WireDirective.php',
>     'Livewire\\Wireable' => $vendorDir . '/livewire/livewire/src/Wireable.php',
>     'Livewire\\WithFileUploads' => $vendorDir . '/livewire/livewire/src/WithFileUploads.php',
>     'Livewire\\WithPagination' => $vendorDir . '/livewire/livewire/src/WithPagination.php',

Changes

<     'Tightenco\\Ziggy\\BladeRouteGenerator' => $vendorDir . '/tightenco/ziggy/src/BladeRouteGenerator.php',
<     'Tightenco\\Ziggy\\CommandRouteGenerator' => $vendorDir . '/tightenco/ziggy/src/CommandRouteGenerator.php',
<     'Tightenco\\Ziggy\\Ziggy' => $vendorDir . '/tightenco/ziggy/src/Ziggy.php',
<     'Tightenco\\Ziggy\\ZiggyServiceProvider' => $vendorDir . '/tightenco/ziggy/src/ZiggyServiceProvider.php',

vendor/composer/autoload_files.php

Changes

<     '98caa11a197f6516a8e48aa4abb5ccc6' => $vendorDir . '/inertiajs/inertia-laravel/helpers.php',
---
>     '40275907c8566c390185147049ef6e5d' => $vendorDir . '/livewire/livewire/src/helpers.php',

vendor/composer/autoload_psr4.php

Changes

<     'Tightenco\\Ziggy\\' => array($vendorDir . '/tightenco/ziggy/src'),

Changes

>     'Livewire\\' => array($vendorDir . '/livewire/livewire/src'),

Changes

<     'Inertia\\' => array($vendorDir . '/inertiajs/inertia-laravel/src'),

vendor/composer/autoload_real.php

Changes

< class ComposerAutoloaderInit1a366ea8c9013f37377c59393191f427
---
> class ComposerAutoloaderInitf2da6e83b085fd96649a1eced6ba9cbb

Changes

<         spl_autoload_register(array('ComposerAutoloaderInit1a366ea8c9013f37377c59393191f427', 'loadClassLoader'), true, true);
---
>         spl_autoload_register(array('ComposerAutoloaderInitf2da6e83b085fd96649a1eced6ba9cbb', 'loadClassLoader'), true, true);

Changes

<         spl_autoload_unregister(array('ComposerAutoloaderInit1a366ea8c9013f37377c59393191f427', 'loadClassLoader'));
---
>         spl_autoload_unregister(array('ComposerAutoloaderInitf2da6e83b085fd96649a1eced6ba9cbb', 'loadClassLoader'));

Changes

<             call_user_func(\Composer\Autoload\ComposerStaticInit1a366ea8c9013f37377c59393191f427::getInitializer($loader));
---
>             call_user_func(\Composer\Autoload\ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::getInitializer($loader));

Changes

<             $includeFiles = Composer\Autoload\ComposerStaticInit1a366ea8c9013f37377c59393191f427::$files;
---
>             $includeFiles = Composer\Autoload\ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::$files;

Changes

<             composerRequire1a366ea8c9013f37377c59393191f427($fileIdentifier, $file);
---
>             composerRequiref2da6e83b085fd96649a1eced6ba9cbb($fileIdentifier, $file);

Changes

< function composerRequire1a366ea8c9013f37377c59393191f427($fileIdentifier, $file)
---
> function composerRequiref2da6e83b085fd96649a1eced6ba9cbb($fileIdentifier, $file)

vendor/composer/autoload_static.php

Changes

< class ComposerStaticInit1a366ea8c9013f37377c59393191f427
---
> class ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb

Changes

<         '98caa11a197f6516a8e48aa4abb5ccc6' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/helpers.php',
---
>         '40275907c8566c390185147049ef6e5d' => __DIR__ . '/..' . '/livewire/livewire/src/helpers.php',

Changes

<             'Tightenco\\Ziggy\\' => 16,

Changes

>             'Livewire\\' => 9,

Changes

<             'Inertia\\' => 8,

Changes

<         'Tightenco\\Ziggy\\' => 
<         array (
<             0 => __DIR__ . '/..' . '/tightenco/ziggy/src',
<         ),

Changes

>         'Livewire\\' => 
>         array (
>             0 => __DIR__ . '/..' . '/livewire/livewire/src',
>         ),

Changes

<         'Inertia\\' => 
<         array (
<             0 => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src',
<         ),

Changes

<         'Inertia\\Console\\CreateMiddleware' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Console/CreateMiddleware.php',
<         'Inertia\\Controller' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Controller.php',
<         'Inertia\\Directive' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Directive.php',
<         'Inertia\\Inertia' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Inertia.php',
<         'Inertia\\LazyProp' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/LazyProp.php',
<         'Inertia\\Middleware' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Middleware.php',
<         'Inertia\\Response' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Response.php',
<         'Inertia\\ResponseFactory' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/ResponseFactory.php',
<         'Inertia\\ServiceProvider' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/ServiceProvider.php',
<         'Inertia\\Ssr\\Gateway' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/Gateway.php',
<         'Inertia\\Ssr\\HttpGateway' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/HttpGateway.php',
<         'Inertia\\Ssr\\Response' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/Response.php',
<         'Inertia\\Testing\\Assert' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Assert.php',
<         'Inertia\\Testing\\AssertableInertia' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/AssertableInertia.php',
<         'Inertia\\Testing\\Concerns\\Debugging' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Debugging.php',
<         'Inertia\\Testing\\Concerns\\Has' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Has.php',
<         'Inertia\\Testing\\Concerns\\Interaction' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Interaction.php',
<         'Inertia\\Testing\\Concerns\\Matching' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Matching.php',
<         'Inertia\\Testing\\Concerns\\PageObject' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/PageObject.php',
<         'Inertia\\Testing\\TestResponseMacros' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/TestResponseMacros.php',

Changes

>         'Livewire\\Castable' => __DIR__ . '/..' . '/livewire/livewire/src/Castable.php',
>         'Livewire\\Commands\\ComponentParser' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/ComponentParser.php',
>         'Livewire\\Commands\\ComponentParserFromExistingComponent' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/ComponentParserFromExistingComponent.php',
>         'Livewire\\Commands\\CopyCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/CopyCommand.php',
>         'Livewire\\Commands\\CpCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/CpCommand.php',
>         'Livewire\\Commands\\DeleteCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/DeleteCommand.php',
>         'Livewire\\Commands\\DiscoverCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/DiscoverCommand.php',
>         'Livewire\\Commands\\FileManipulationCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/FileManipulationCommand.php',
>         'Livewire\\Commands\\MakeCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/MakeCommand.php',
>         'Livewire\\Commands\\MakeLivewireCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/MakeLivewireCommand.php',
>         'Livewire\\Commands\\MoveCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/MoveCommand.php',
>         'Livewire\\Commands\\MvCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/MvCommand.php',
>         'Livewire\\Commands\\PublishCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/PublishCommand.php',
>         'Livewire\\Commands\\RmCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/RmCommand.php',
>         'Livewire\\Commands\\S3CleanupCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/S3CleanupCommand.php',
>         'Livewire\\Commands\\StubParser' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/StubParser.php',
>         'Livewire\\Commands\\StubsCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/StubsCommand.php',
>         'Livewire\\Commands\\TouchCommand' => __DIR__ . '/..' . '/livewire/livewire/src/Commands/TouchCommand.php',
>         'Livewire\\CompilerEngineForIgnition' => __DIR__ . '/..' . '/livewire/livewire/src/CompilerEngineForIgnition.php',
>         'Livewire\\Component' => __DIR__ . '/..' . '/livewire/livewire/src/Component.php',
>         'Livewire\\ComponentChecksumManager' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentChecksumManager.php',
>         'Livewire\\ComponentConcerns\\HandlesActions' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/HandlesActions.php',
>         'Livewire\\ComponentConcerns\\InteractsWithProperties' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/InteractsWithProperties.php',
>         'Livewire\\ComponentConcerns\\PerformsRedirects' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/PerformsRedirects.php',
>         'Livewire\\ComponentConcerns\\ReceivesEvents' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/ReceivesEvents.php',
>         'Livewire\\ComponentConcerns\\RendersLivewireComponents' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/RendersLivewireComponents.php',
>         'Livewire\\ComponentConcerns\\TracksRenderedChildren' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/TracksRenderedChildren.php',
>         'Livewire\\ComponentConcerns\\ValidatesInput' => __DIR__ . '/..' . '/livewire/livewire/src/ComponentConcerns/ValidatesInput.php',
>         'Livewire\\Connection\\ConnectionHandler' => __DIR__ . '/..' . '/livewire/livewire/src/Connection/ConnectionHandler.php',
>         'Livewire\\Controllers\\CanPretendToBeAFile' => __DIR__ . '/..' . '/livewire/livewire/src/Controllers/CanPretendToBeAFile.php',
>         'Livewire\\Controllers\\FilePreviewHandler' => __DIR__ . '/..' . '/livewire/livewire/src/Controllers/FilePreviewHandler.php',
>         'Livewire\\Controllers\\FileUploadHandler' => __DIR__ . '/..' . '/livewire/livewire/src/Controllers/FileUploadHandler.php',
>         'Livewire\\Controllers\\HttpConnectionHandler' => __DIR__ . '/..' . '/livewire/livewire/src/Controllers/HttpConnectionHandler.php',
>         'Livewire\\Controllers\\LivewireJavaScriptAssets' => __DIR__ . '/..' . '/livewire/livewire/src/Controllers/LivewireJavaScriptAssets.php',
>         'Livewire\\CreateBladeView' => __DIR__ . '/..' . '/livewire/livewire/src/CreateBladeView.php',
>         'Livewire\\DisableBrowserCache' => __DIR__ . '/..' . '/livewire/livewire/src/DisableBrowserCache.php',
>         'Livewire\\Event' => __DIR__ . '/..' . '/livewire/livewire/src/Event.php',
>         'Livewire\\Exceptions\\BypassViewHandler' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/BypassViewHandler.php',
>         'Livewire\\Exceptions\\CannotBindToModelDataWithoutValidationRuleException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/CannotBindToModelDataWithoutValidationRuleException.php',
>         'Livewire\\Exceptions\\CannotUseReservedLivewireComponentProperties' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/CannotUseReservedLivewireComponentProperties.php',
>         'Livewire\\Exceptions\\ComponentAttributeMissingOnDynamicComponentException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/ComponentAttributeMissingOnDynamicComponentException.php',
>         'Livewire\\Exceptions\\ComponentNotFoundException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/ComponentNotFoundException.php',
>         'Livewire\\Exceptions\\CorruptComponentPayloadException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/CorruptComponentPayloadException.php',
>         'Livewire\\Exceptions\\DirectlyCallingLifecycleHooksNotAllowedException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/DirectlyCallingLifecycleHooksNotAllowedException.php',
>         'Livewire\\Exceptions\\LivewirePageExpiredBecauseNewDeploymentHasSignificantEnoughChanges' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/LivewirePageExpiredBecauseNewDeploymentHasSignificantEnoughChanges.php',
>         'Livewire\\Exceptions\\MethodNotFoundException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/MethodNotFoundException.php',
>         'Livewire\\Exceptions\\MissingFileUploadsTraitException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/MissingFileUploadsTraitException.php',
>         'Livewire\\Exceptions\\MissingRulesException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/MissingRulesException.php',
>         'Livewire\\Exceptions\\NonPublicComponentMethodCall' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/NonPublicComponentMethodCall.php',
>         'Livewire\\Exceptions\\PropertyNotFoundException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/PropertyNotFoundException.php',
>         'Livewire\\Exceptions\\PublicPropertyNotFoundException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/PublicPropertyNotFoundException.php',
>         'Livewire\\Exceptions\\PublicPropertyTypeNotAllowedException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/PublicPropertyTypeNotAllowedException.php',
>         'Livewire\\Exceptions\\RootTagMissingFromViewException' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/RootTagMissingFromViewException.php',
>         'Livewire\\Exceptions\\S3DoesntSupportMultipleFileUploads' => __DIR__ . '/..' . '/livewire/livewire/src/Exceptions/S3DoesntSupportMultipleFileUploads.php',
>         'Livewire\\Features\\OptimizeRenderedDom' => __DIR__ . '/..' . '/livewire/livewire/src/Features/OptimizeRenderedDom.php',
>         'Livewire\\Features\\Placeholder' => __DIR__ . '/..' . '/livewire/livewire/src/Features/Placeholder.php',
>         'Livewire\\Features\\SupportActionReturns' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportActionReturns.php',
>         'Livewire\\Features\\SupportBootMethod' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportBootMethod.php',
>         'Livewire\\Features\\SupportBrowserHistory' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportBrowserHistory.php',
>         'Livewire\\Features\\SupportChildren' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportChildren.php',
>         'Livewire\\Features\\SupportCollections' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportCollections.php',
>         'Livewire\\Features\\SupportComponentTraits' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportComponentTraits.php',
>         'Livewire\\Features\\SupportDateTimes' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportDateTimes.php',
>         'Livewire\\Features\\SupportEvents' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportEvents.php',
>         'Livewire\\Features\\SupportFileDownloads' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportFileDownloads.php',
>         'Livewire\\Features\\SupportFileUploads' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportFileUploads.php',
>         'Livewire\\Features\\SupportLocales' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportLocales.php',
>         'Livewire\\Features\\SupportPostDeploymentInvalidation' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportPostDeploymentInvalidation.php',
>         'Livewire\\Features\\SupportRedirects' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportRedirects.php',
>         'Livewire\\Features\\SupportRootElementTracking' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportRootElementTracking.php',
>         'Livewire\\Features\\SupportStacks' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportStacks.php',
>         'Livewire\\Features\\SupportValidation' => __DIR__ . '/..' . '/livewire/livewire/src/Features/SupportValidation.php',
>         'Livewire\\FileUploadConfiguration' => __DIR__ . '/..' . '/livewire/livewire/src/FileUploadConfiguration.php',
>         'Livewire\\GenerateSignedUploadUrl' => __DIR__ . '/..' . '/livewire/livewire/src/GenerateSignedUploadUrl.php',
>         'Livewire\\HydrationMiddleware\\AddAttributesToRootTagOfHtml' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/AddAttributesToRootTagOfHtml.php',
>         'Livewire\\HydrationMiddleware\\CallHydrationHooks' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/CallHydrationHooks.php',
>         'Livewire\\HydrationMiddleware\\CallPropertyHydrationHooks' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/CallPropertyHydrationHooks.php',
>         'Livewire\\HydrationMiddleware\\HashDataPropertiesForDirtyDetection' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/HashDataPropertiesForDirtyDetection.php',
>         'Livewire\\HydrationMiddleware\\HydratePublicProperties' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php',
>         'Livewire\\HydrationMiddleware\\HydrationMiddleware' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/HydrationMiddleware.php',
>         'Livewire\\HydrationMiddleware\\NormalizeComponentPropertiesForJavaScript' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/NormalizeComponentPropertiesForJavaScript.php',
>         'Livewire\\HydrationMiddleware\\NormalizeDataForJavaScript' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/NormalizeDataForJavaScript.php',
>         'Livewire\\HydrationMiddleware\\NormalizeServerMemoSansDataForJavaScript' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/NormalizeServerMemoSansDataForJavaScript.php',
>         'Livewire\\HydrationMiddleware\\PerformActionCalls' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/PerformActionCalls.php',
>         'Livewire\\HydrationMiddleware\\PerformDataBindingUpdates' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/PerformDataBindingUpdates.php',
>         'Livewire\\HydrationMiddleware\\PerformEventEmissions' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/PerformEventEmissions.php',
>         'Livewire\\HydrationMiddleware\\RenderView' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/RenderView.php',
>         'Livewire\\HydrationMiddleware\\SecureHydrationWithChecksum' => __DIR__ . '/..' . '/livewire/livewire/src/HydrationMiddleware/SecureHydrationWithChecksum.php',
>         'Livewire\\ImplicitRouteBinding' => __DIR__ . '/..' . '/livewire/livewire/src/ImplicitRouteBinding.php',
>         'Livewire\\ImplicitlyBoundMethod' => __DIR__ . '/..' . '/livewire/livewire/src/ImplicitlyBoundMethod.php',
>         'Livewire\\LifecycleManager' => __DIR__ . '/..' . '/livewire/livewire/src/LifecycleManager.php',
>         'Livewire\\Livewire' => __DIR__ . '/..' . '/livewire/livewire/src/Livewire.php',
>         'Livewire\\LivewireBladeDirectives' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireBladeDirectives.php',
>         'Livewire\\LivewireComponentsFinder' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireComponentsFinder.php',
>         'Livewire\\LivewireManager' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireManager.php',
>         'Livewire\\LivewireServiceProvider' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireServiceProvider.php',
>         'Livewire\\LivewireTagCompiler' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireTagCompiler.php',
>         'Livewire\\LivewireViewCompilerEngine' => __DIR__ . '/..' . '/livewire/livewire/src/LivewireViewCompilerEngine.php',
>         'Livewire\\Macros\\DuskBrowserMacros' => __DIR__ . '/..' . '/livewire/livewire/src/Macros/DuskBrowserMacros.php',
>         'Livewire\\Macros\\ViewMacros' => __DIR__ . '/..' . '/livewire/livewire/src/Macros/ViewMacros.php',
>         'Livewire\\ObjectPrybar' => __DIR__ . '/..' . '/livewire/livewire/src/ObjectPrybar.php',
>         'Livewire\\Redirector' => __DIR__ . '/..' . '/livewire/livewire/src/Redirector.php',
>         'Livewire\\Request' => __DIR__ . '/..' . '/livewire/livewire/src/Request.php',
>         'Livewire\\Response' => __DIR__ . '/..' . '/livewire/livewire/src/Response.php',
>         'Livewire\\TemporaryUploadedFile' => __DIR__ . '/..' . '/livewire/livewire/src/TemporaryUploadedFile.php',
>         'Livewire\\Testing\\Concerns\\HasFunLittleUtilities' => __DIR__ . '/..' . '/livewire/livewire/src/Testing/Concerns/HasFunLittleUtilities.php',
>         'Livewire\\Testing\\Concerns\\MakesAssertions' => __DIR__ . '/..' . '/livewire/livewire/src/Testing/Concerns/MakesAssertions.php',
>         'Livewire\\Testing\\Concerns\\MakesCallsToComponent' => __DIR__ . '/..' . '/livewire/livewire/src/Testing/Concerns/MakesCallsToComponent.php',
>         'Livewire\\Testing\\MakesHttpRequestsWrapper' => __DIR__ . '/..' . '/livewire/livewire/src/Testing/MakesHttpRequestsWrapper.php',
>         'Livewire\\Testing\\TestableLivewire' => __DIR__ . '/..' . '/livewire/livewire/src/Testing/TestableLivewire.php',
>         'Livewire\\WireDirective' => __DIR__ . '/..' . '/livewire/livewire/src/WireDirective.php',
>         'Livewire\\Wireable' => __DIR__ . '/..' . '/livewire/livewire/src/Wireable.php',
>         'Livewire\\WithFileUploads' => __DIR__ . '/..' . '/livewire/livewire/src/WithFileUploads.php',
>         'Livewire\\WithPagination' => __DIR__ . '/..' . '/livewire/livewire/src/WithPagination.php',

Changes

<         'Tightenco\\Ziggy\\BladeRouteGenerator' => __DIR__ . '/..' . '/tightenco/ziggy/src/BladeRouteGenerator.php',
<         'Tightenco\\Ziggy\\CommandRouteGenerator' => __DIR__ . '/..' . '/tightenco/ziggy/src/CommandRouteGenerator.php',
<         'Tightenco\\Ziggy\\Ziggy' => __DIR__ . '/..' . '/tightenco/ziggy/src/Ziggy.php',
<         'Tightenco\\Ziggy\\ZiggyServiceProvider' => __DIR__ . '/..' . '/tightenco/ziggy/src/ZiggyServiceProvider.php',

Changes

<             $loader->prefixLengthsPsr4 = ComposerStaticInit1a366ea8c9013f37377c59393191f427::$prefixLengthsPsr4;
<             $loader->prefixDirsPsr4 = ComposerStaticInit1a366ea8c9013f37377c59393191f427::$prefixDirsPsr4;
<             $loader->prefixesPsr0 = ComposerStaticInit1a366ea8c9013f37377c59393191f427::$prefixesPsr0;
<             $loader->classMap = ComposerStaticInit1a366ea8c9013f37377c59393191f427::$classMap;
---
>             $loader->prefixLengthsPsr4 = ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::$prefixLengthsPsr4;
>             $loader->prefixDirsPsr4 = ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::$prefixDirsPsr4;
>             $loader->prefixesPsr0 = ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::$prefixesPsr0;
>             $loader->classMap = ComposerStaticInitf2da6e83b085fd96649a1eced6ba9cbb::$classMap;
## vendor/composer/installed.json

Changes

<             "name": "inertiajs/inertia-laravel",
<             "version": "v0.5.2",
<             "version_normalized": "0.5.2.0",
<             "source": {
<                 "type": "git",
<                 "url": "https://github.com/inertiajs/inertia-laravel.git",
<                 "reference": "9c8c4201435aa0c11cb832242cf4c1b01bd8ef32"
<             },
<             "dist": {
<                 "type": "zip",
<                 "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/9c8c4201435aa0c11cb832242cf4c1b01bd8ef32",
<                 "reference": "9c8c4201435aa0c11cb832242cf4c1b01bd8ef32",
<                 "shasum": ""
<             },
<             "require": {
<                 "ext-json": "*",
<                 "laravel/framework": "^6.0|^7.0|^8.74|^9.0",
<                 "php": "^7.2|~8.0.0|~8.1.0"
<             },
<             "require-dev": {
<                 "mockery/mockery": "^1.3.3",
<                 "orchestra/testbench": "^4.0|^5.0|^6.4|^7.0",
<                 "phpunit/phpunit": "^8.0|^9.5.8",
<                 "roave/security-advisories": "dev-master"
<             },
<             "time": "2022-01-12T16:18:26+00:00",
<             "type": "library",
<             "extra": {
<                 "laravel": {
<                     "providers": [
<                         "Inertia\\ServiceProvider"
<                     ]
<                 }
<             },
<             "installation-source": "dist",
<             "autoload": {
<                 "psr-4": {
<                     "Inertia\\": "src"
<                 },
<                 "files": [
<                     "./helpers.php"
<                 ]
<             },
<             "notification-url": "https://packagist.org/downloads/",
<             "license": [
<                 "MIT"
<             ],
<             "authors": [
<                 {
<                     "name": "Jonathan Reinink",
<                     "email": "jonathan@reinink.ca",
<                     "homepage": "https://reinink.ca"
<                 }
<             ],
<             "description": "The Laravel adapter for Inertia.js.",
<             "keywords": [
<                 "inertia",
<                 "laravel"
<             ],
<             "support": {
<                 "issues": "https://github.com/inertiajs/inertia-laravel/issues",
<                 "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.2"
<             },
<             "funding": [
<                 {
<                     "url": "https://github.com/reinink",
<                     "type": "github"
<                 }
<             ],
<             "install-path": "../inertiajs/inertia-laravel"
<         },
<         {

Changes

>             "name": "livewire/livewire",
>             "version": "v2.9.0",
>             "version_normalized": "2.9.0.0",
>             "source": {
>                 "type": "git",
>                 "url": "https://github.com/livewire/livewire.git",
>                 "reference": "e117c78f9a4b19edb294b5b576138fd1f896925a"
>             },
>             "dist": {
>                 "type": "zip",
>                 "url": "https://api.github.com/repos/livewire/livewire/zipball/e117c78f9a4b19edb294b5b576138fd1f896925a",
>                 "reference": "e117c78f9a4b19edb294b5b576138fd1f896925a",
>                 "shasum": ""
>             },
>             "require": {
>                 "illuminate/database": "^7.0|^8.0",
>                 "illuminate/support": "^7.0|^8.0",
>                 "illuminate/validation": "^7.0|^8.0",
>                 "php": "^7.2.5|^8.0",
>                 "symfony/http-kernel": "^5.0"
>             },
>             "require-dev": {
>                 "calebporzio/sushi": "^2.1",
>                 "laravel/framework": "^7.0|^8.0",
>                 "mockery/mockery": "^1.3.1",
>                 "orchestra/testbench": "^5.0|^6.0",
>                 "orchestra/testbench-dusk": "^5.2|^6.0",
>                 "phpunit/phpunit": "^8.4|^9.0",
>                 "psy/psysh": "@stable"
>             },
>             "time": "2022-01-13T20:07:05+00:00",
>             "type": "library",
>             "extra": {
>                 "laravel": {
>                     "providers": [
>                         "Livewire\\LivewireServiceProvider"
>                     ],
>                     "aliases": {
>                         "Livewire": "Livewire\\Livewire"
>                     }
>                 }
>             },
>             "installation-source": "dist",
>             "autoload": {
>                 "files": [
>                     "src/helpers.php"
>                 ],
>                 "psr-4": {
>                     "Livewire\\": "src/"
>                 }
>             },
>             "notification-url": "https://packagist.org/downloads/",
>             "license": [
>                 "MIT"
>             ],
>             "authors": [
>                 {
>                     "name": "Caleb Porzio",
>                     "email": "calebporzio@gmail.com"
>                 }
>             ],
>             "description": "A front-end framework for Laravel.",
>             "support": {
>                 "issues": "https://github.com/livewire/livewire/issues",
>                 "source": "https://github.com/livewire/livewire/tree/v2.9.0"
>             },
>             "funding": [
>                 {
>                     "url": "https://github.com/livewire",
>                     "type": "github"
>                 }
>             ],
>             "install-path": "../livewire/livewire"
>         },
>         {

Changes

<             "name": "tightenco/ziggy",
<             "version": "v1.4.2",
<             "version_normalized": "1.4.2.0",
<             "source": {
<                 "type": "git",
<                 "url": "https://github.com/tighten/ziggy.git",
<                 "reference": "620c135281062b9f6b53a75b07f99a4339267277"
<             },
<             "dist": {
<                 "type": "zip",
<                 "url": "https://api.github.com/repos/tighten/ziggy/zipball/620c135281062b9f6b53a75b07f99a4339267277",
<                 "reference": "620c135281062b9f6b53a75b07f99a4339267277",
<                 "shasum": ""
<             },
<             "require": {
<                 "laravel/framework": ">=5.4@dev"
<             },
<             "require-dev": {
<                 "orchestra/testbench": "^6.0",
<                 "phpunit/phpunit": "^9.2"
<             },
<             "time": "2021-10-01T13:55:26+00:00",
<             "type": "library",
<             "extra": {
<                 "laravel": {
<                     "providers": [
<                         "Tightenco\\Ziggy\\ZiggyServiceProvider"
<                     ]
<                 }
<             },
<             "installation-source": "dist",
<             "autoload": {
<                 "psr-4": {
<                     "Tightenco\\Ziggy\\": "src/"
<                 }
<             },
<             "notification-url": "https://packagist.org/downloads/",
<             "license": [
<                 "MIT"
<             ],
<             "authors": [
<                 {
<                     "name": "Daniel Coulbourne",
<                     "email": "daniel@tighten.co"
<                 },
<                 {
<                     "name": "Jake Bathman",
<                     "email": "jake@tighten.co"
<                 },
<                 {
<                     "name": "Jacob Baker-Kretzmar",
<                     "email": "jacob@tighten.co"
<                 }
<             ],
<             "description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.",
<             "homepage": "https://github.com/tighten/ziggy",
<             "keywords": [
<                 "Ziggy",
<                 "javascript",
<                 "laravel",
<                 "routes"
<             ],
<             "support": {
<                 "issues": "https://github.com/tighten/ziggy/issues",
<                 "source": "https://github.com/tighten/ziggy/tree/v1.4.2"
<             },
<             "install-path": "../tightenco/ziggy"
<         },
<         {
## vendor/composer/installed.php

Changes

<         'inertiajs/inertia-laravel' => array(
<             'pretty_version' => 'v0.5.2',
<             'version' => '0.5.2.0',
<             'type' => 'library',
<             'install_path' => __DIR__ . '/../inertiajs/inertia-laravel',
<             'aliases' => array(),
<             'reference' => '9c8c4201435aa0c11cb832242cf4c1b01bd8ef32',
<             'dev_requirement' => false,
<         ),

Changes

>         'livewire/livewire' => array(
>             'pretty_version' => 'v2.9.0',
>             'version' => '2.9.0.0',
>             'type' => 'library',
>             'install_path' => __DIR__ . '/../livewire/livewire',
>             'aliases' => array(),
>             'reference' => 'e117c78f9a4b19edb294b5b576138fd1f896925a',
>             'dev_requirement' => false,
>         ),

Changes

<         'tightenco/ziggy' => array(
<             'pretty_version' => 'v1.4.2',
<             'version' => '1.4.2.0',
<             'type' => 'library',
<             'install_path' => __DIR__ . '/../tightenco/ziggy',
<             'aliases' => array(),
<             'reference' => '620c135281062b9f6b53a75b07f99a4339267277',
<             'dev_requirement' => false,
<         ),

## Only invendor: inertiajs

## Only inlaravel-starter-jetstream-livewire/vendor: livewire

## Only invendor: tightenco

## Only inlaravel-starter-jetstream-inertia: webpack.config.js

webpack.mix.js

Changes

< mix.js('resources/js/app.js', 'public/js').vue()
---
> mix.js('resources/js/app.js', 'public/js')

Changes

<     ])
<     .webpackConfig(require('./webpack.config'));
---
>     ]);
Laravel Toolbox

Laravel | Tipps und Tricks

Starter

Create Laravel Starter with basic functionalities

laravel new --jet --stack livewire --teams app

Views

Extend the file resources/views/navigation-menu.blade.php

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
   <x-jet-nav-link href="{{ route('dashboard') }}"
                   :active="request()->routeIs('dashboard')">
      { __('Dashboard') }}
   </x-jet-nav-link>
</div>

<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
   <x-jet-nav-link href="{{ route('particles') }}"
                   :active="request()->routeIs('particles')">
      {{ __('Particles') }}
   </x-jet-nav-link>
</div>

Display Laravel and PHP Version

<div>
    Laravel v{{ Illuminate\Foundation\Application::VERSION }}
    (PHP v{{ PHP_VERSION }})
</div>

Create new View and Component

php artisan make:component NewComponent

Creates

app/View/Components/NewComponent.php

and

resources/views/components/new-component.blade.php

Command Line

Create new command make:view

Original source is here

php artisan make:command MakeViewCommand

Create the following file

app/Console/Commands/MakeViewCommand.php

Edit the file and overwrite code with the following

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use File;

class MakeViewCommand extends Command
{
    protected $signature = 'make:view {view}';
    protected $description = 'Create a new blade template.';
    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $view = $this->argument('view');
        $path = $this->viewPath($view);

        $this->createDir($path);

        if (File::exists($path))
        {
            $this->error("File {$path} already exists!");
            return;
        }

        File::put($path, $path);

        $this->info("File {$path} created.");
    }

    public function viewPath($view)
    {
        $view = str_replace('.', '/', $view) . '.blade.php';

        return "resources/views/{$view}";
    }

    public function createDir($path)
    {
        $dir = dirname($path);

        if (!file_exists($dir))
        {
            mkdir($dir, 0777, true);
        }
    }

}

Database

SQLite

Create an empty SQLite Database

sqlite3 database.sqlite "create table t(f int); drop table t;"

https://laravel-news.com/
https://laravel-news.com/learning-laravel-in-2021
https://laravel.com/docs/8.x

https://laravel-livewire.com/screencasts/installation

https://www.larashout.com/

Tutorial

https://www.tutsmake.com/category/laravel-tutorial/
https://www.tutsmake.com/laravel-interview-questions-answers-for-1235-year-experience/

https://learn2torials.com/category/laravel

https://kinsta.com/blog/laravel-tutorial/#6-best-free-laravel-tutorial-sites

Database

https://eloquentbyexample.com

https://laravel.com/docs/8.x/eloquent#introduction

Blade

https://www.a-coding-project.de/ratgeber/laravel/blade

Blog erstellen

https://www.flowkl.com/tutorial/web-development/simple-blog-application-in-laravel-7/

https://www.section.io/engineering-education/laravel-beginners-guide-blogpost/

https://medium.com/@dinyangetoh/how-to-build-a-blog-with-laravel-9f735d1f3116

https://medium.com/@dinyangetoh/how-to-build-a-blog-with-laravel-9f735d1f3116