Laravel Mailhog Docker

Letztens wurde ich gefragt, wie ich Mail bei Laravel teste. Ich meinte ich habe einen SMTP Server den ich nutzen kann. Da meinte er, dass wäre zwar okay aber man könnte sich damit auch eine valide Mailadresse verbrennen. Mit verbrennen meint der Kollege, dass sie global als SPAM Mailadresse angerechnet werden kann. Falls die Tests mal ausufern sollten. Hmm. So richtig überzeugt hatte es mich jetzt nicht, aber ich hab mich seiner Empfehlung Mailhog zu nutzen nicht wiedersetzt. Im Gegenteil ich sogar ganz dankbar dafür. Weil wann hätte ich mit dieser Gewohnheit sonst gebrochen.

Mailhog ist ein lokal Dienst. Man kann sich diesen auf den Rechner Installieren. Dieser Dienst kommuniziert über den SMTP Standard Port 1025. Das bedeutet wenn wir unsere Laravel Konfiguration in der .env auf mailhog anpassen, senden wir aus der Applikation an unseren lokalen SMTP Server. Mailhog hat auch ein User Interface mit dem wir die Mails dann anzeigen lassen können.

Der einfachste Weg Mailhog zu nutzen ist über Docker. Dafür müssen wir nur:

docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog

eingeben.

In unserer .env Datei passen wir noch die SMTP Konfiguration an:

MAIL_MAILER=smtp
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=hello@world.com
MAIL_FROM_NAME="${APP_NAME}"

Dein “Fake” Mailhog EmailClient kannst über localhost:8025 erreichen.

Wenn Du deine gesamte Umgebung über ein docker-compose laufen lässt kannst du natürlich auch den Mailhog SMTP Server mit hinzu fügen.

services:
   mailhog:
     image: mailhog/mailhog:latest
     restart: always
     ports:
       - 1025:1025
       - 8025:8025

Ein gesamte docker-compose Laravel Umgebung findet ihr hier:
https://github.com/MikeLowrey/docker-compose-laravel-2021

. Ansonsten findest Du eine ausführlichere Mailhog Anleitung unter: https://kinsta.com/de/blog/mailhog/.

MySQL über die Console

Vielleicht geht es euch ja wie mir. Ständig vergesse ich die MySQL Commands. Schnell kommt es vor, dass man in einer MySQL Docker Instanz etwas an der Datenbank machen muss. In meinem Fall, der mich dazu brachte es ein für alle mal die wichtigsten MySQL Commands niederzuschreiben, war es, dass meine Docker App ständig meinte er kenne die Datenbank nicht.

Logt euch über die CLI auf eure MySQL Docker Instanz ein.

docker exec -it laravel-app-mysql bash
 mysql -u root -p
 SHOW DATABASES; // Zeigt dir alle Datenbanken an
 +--------------------+
 |     Databases      |
 +--------------------+
 | information_schema |
 | database_name      |
 | mysql              |
 | opencart           |
 | laravel          |
 | performance_schema |
 | sys                |
 +--------------------+
 7 rows in set (0.00 sec)
 SHOW DATABASES LIKE 'lara%'; // Zeigt dir die Datenbanken die auf lara matchen.
 USE laravel; // Loggt dich in die laravel DB ein
 SHOW Tables; / Zeigt dir alle Tabellen an

Starte und Stoppe dein Mysql Service:

service mysql start

service mysql stop

Docker: Laravel 8 mit PHP 8

Moinsen Leute! Laravel 8 mit PHP 8 auf einem nginx mit node 14 laufen lassen. Wer das benötigt, der sollte folgende docker config nutzen.

FROM php:8.0-fpm
 Arguments defined in docker-compose.yml
 ARG user
 ARG uid
 Install system dependencies
 RUN apt-get update && apt-get install -y \
     git \
     curl \
     libpng-dev \
     libonig-dev \
     libxml2-dev \
     zip \
     unzip
 Clear cache
 RUN apt-get clean && rm -rf /var/lib/apt/lists/*
 Install PHP extensions
 RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
 Get latest Composer
 COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
 Create system user to run Composer and Artisan Commands
 RUN useradd -G www-data,root -u $uid -d /home/$user $user
 RUN mkdir -p /home/$user/.composer && \
     chown -R $user:$user /home/$user
 Install Nodejs
 RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
 RUN apt-get install -y nodejs
 Set working directory
 WORKDIR /var/www
 USER $user

Wer eine andere Node Version braucht, der schaut hier nach welche er braucht und kopiert den Link hier zur geeigneten Nodejs Distribution rein.

Und hier die Docker-compose.yml Datei.

version: "3.7"
 services:
 Laravel App
 app:
     build:
       args:
         user: martin
         uid: 1000
       context: ./
       dockerfile: Dockerfile
     image: laravel-app
     container_name: laravel-api-app
     restart: unless-stopped
     working_dir: /var/www/
     volumes:
       - ./:/var/www
     networks:
       - mynetwork
 #Nginx Service
   nginx:
     image: nginx:alpine
     container_name: laravel-api-nginx
     restart: unless-stopped
     ports:
       - 8000:80
     volumes:
       - ./:/var/www
       - ./nginx/conf.d:/etc/nginx/conf.d/
     networks:
       - mynetwork
   #Mysl Service
 networks:
   mynetwork:
     driver: bridge

Lege einen nginx Ordner und eine Datei conf an:

mkdir -p nginx/conf
server {
     listen 80;
     index index.php index.html;
     error_log  /var/log/nginx/error.log;
     access_log /var/log/nginx/access.log;
     root /var/www/application/public;
     location ~ .php$ {
         try_files $uri =404;
         fastcgi_split_path_info ^(.+.php)(/.+)$;
         fastcgi_pass app:9000;
         fastcgi_index index.php;
         include fastcgi_params;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
         fastcgi_param PATH_INFO $fastcgi_path_info;
     }
     location / {
         try_files $uri $uri/ /index.php?$query_string;
         gzip_static on;
     }
 }

Hier kannst Du dir meine Docker-Compose YML ziehen und bauen. Du erhälst eine sehr geeignete Umgebung für eine Laravel, VueJS Applikation. Mit dabei sind: nginx, mysql, php 8, node, npm, vim , composer.

https://github.com/MikeLowrey/docker-compose-laravel-2021

Git Remote Eintrag löschen

Ihr habt ein lokales Repository und ein Remote Repository. Das Remote Repository liegt zum Beispiel bei GitHub und ihr wollt es nun aber bei GitLab unterbringen. Wie löscht man jetzt die Remote Config im lokalen Git Projekt Verzeichnis. Falls ihr noch nicht in dem gewünschten Verzeichnis seid, dann aber schnell hin da.

Mit git status checkt ihr noch mal ob ihr im richtigen Ordner seid.
Mit git remote -v werden dir alle Fernbeziehungen unserer lokalen Repositories angezeigt.

origin git@github.com:SeoTheater/xxx-yyy.git (fetch)
origin git@github.com:SeoTheater/xxx-yyy.git (push)

Und nun könnt ihr mit

git remote rm origin

die Remotebeziehung zu eurem GiHub oder Ähnlichem beenden. Den: “Nichts ist für die Ewigkeit.”

Viel Spaß beim UnRemoten!

Drei nützliche JavaScript Object Funktionen

Es gibt im JavaScript viele nützliche Array Funktionen aber wiederum gelten die nicht für Objekte. Das kann zum Beispiel ein Grund sein, warum man sich einer Object Funktion bedient um daraus ein Array zu machen. Ich stelle im Folgenden die Funktionen: Object.keys, Object.value und Object.entries vor.

Ausgangsituation wäre ein nettes Objekt:

const niceObject = { 
     2019: {visitors:1}, 
     2020: {visitors:2}, 
     2021: {visitors:4}
 }

Das sind nämlich die Unique Besucher meiner Website und nun möchte ich errechnen wieviele Besucher ich seit 2019 hatte. Mit Array.reduce() wäre das im Handumdrehen gemacht. Aber mit einem Object. Wie geht den das?

Object.keys

Objekt.keys gibt uns alle Keys des Objektes als Array wieder.

const years = Object.keys(niceObject)
// output years
[2019, 2020, 2021]

Nun können wir mit der Arrayfunktion reduce() was anfangen. Ist doch ein Array.

const result = years.reduce((sum, key) => {
    return sum + niceObject[key].visitors;
}, 0)
// output result
7

Object.values

Object.values gibt uns ein Array mit allen (drei Objekten) Values zurück.

const visitors = Object.values(niceObject)
 // output visitors
 [
   {visitors: 1},
   {visitors: 2},
   {visitors: 4},
 ]

Deshalb können wir nun folgendes machen:

const result = visitors.reduce((sum, singleObject) => {
   return sum + singleObject.visitors;
}, 0);
// output result
7

// Oder richtig kurz
const resultShortObject.values(niceObject).reduce((t, n) => t + n.visitors, 0)
// output resultShort
7

Object.entries

Object.entries gibt uns ein Array mit drei Arrays zurück.

const entriesArray = Object.entries(niceObject)
// output entriesArray
[
  ["a", {visitors: 1}],
  ["b", {visitors: 2}],
  ["c", {visitors: 4}]
];
let result = entriesArray.reduce((sum, arr) => {
  return sum + arr[1].visitors;
}, 0)
// output result
7

Also 7 Besucher in drei Jahren. Not bad! Aber ich war schon mal besser…

Bei alle drei Funktionen übergeben wir einen Parameter mit dem Wert 0. Dieser ist der Initial Wert.

Docker GUI – Portainer easy und übersichtlich

Klar, ich mag die Kommandozeile. Für alle möglichen Aufgaben ist sie mein zuverlässiger Begleiter. So auch im Docker Umfeld. Die Commands sind eigentlich einfach aber man vergisst schnell wieder einiges und muss dann im Netz Nachschlagen. Vielleicht geht es euch ja genau so?

Und für Docker gibt es eine freie und wirklich gute GUI. Sie heißt Portainer und ist einfach zu installieren und zu handhaben.

Installieren mit:

docker run -d -p 9000:9000 --name=portainer -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

Nutzen unter:

http://localhost:9000

Ihr solltet es so wie jetzt auf jeden Fall nur Lokal beieuch nutzen. Falls ihr es in einem Netzwerk betreibt wäre eine HTTP Verschlüsselung zwingend nutwendig. Das wars auch schon!

Meine wichtigsten Artisan Commands

Als Laravel Entwickler braucht man sie ständig. Wenn man etwas länger Frontendarbeiten gemacht hat, kommt es schon mal vor, dass man etwas vergessen hat. Daher hier die Liste mit den häufigsten Artisan Commands:

php artisan migrate:rollback --step=1

Ihr wollte eine oder x-beliebige Migrationen zurückspulen? Dann rollback und die Anzahl der Migrationssteps eingeben.

php artisan migrate:refresh

Wem alles egal ist, der setzt alle Migrationen zurück und spielt sie neu ein. Ein häufiger Command beim Laravel Entwicklen.

php artisan migrate:refresh --seed

Migrationen zurücksetzen und neu Einspielen und dann noch Daten seeden. Das ist einer meiner Lieblingscommands.

php artisan test

Startet die Testreihe. Feature und Unit Tests werden durchgerödelt.

php artisan test --testsuite=Unit

Wer nur die Unittests laufen lassen möchte, der gibt den Parameter testsuite mit. Als Value entweder Unit oder Feature.

php artisan make:seeder AreaSeeder

Um ein Seeder zu erstellen

php artisan make:factory StateFactory --model=State

Um ein Factory zu erstellen. Mit dem Parameter model gibst Du das Model an, welches artisan gleich in die Factory lädt. Eine Zeile weniger zu schreiben halt.

php artisan route:list

Zeigt dir alle Routes in deinem aktuellen Projekt.

php artisan config:cache

Leert Dir den Cache und den Config Cache. Braucht man bei Änderungen auf der Konfigurationsebene.

php artisan make:model State -m

Erstellt dir ein neues Model. Mit dem optionalen Parameter -m gibst Du an, ob artisan dir auch gleich noch eine Migration erstellen soll. Eigentlich braucht man fast immer zu einem Model eine Migration, oder?

php artisan make:provider HelperServiceProvider

Erstellt Dir ein Provider. In diesem Fall einen mit dem Namen HelperServiceProvider. Diesen legt er dir an ins entsprechende Verzeichnis.

php artisan make:test AreaTest --unit

Erstellt dir einen Test. Mit dem Parameter –unit gibst Du an, dass es sich um ein Unittest handelt. Artisan legt dir auch den Test in den entsprechenden Ordner. Ohne –unit als Parameter, wird es immer ein Featuretest.

php artisan make:controller TestController

Erstellt Dir ein Controller mit dem Namen Testcontroller.

php artisan make:migration create_post_tag_table

Erstellt Dir eine Migrationsdatei.

php artisan make:model -mc Area --resource

Model, Controler und Migration mit einem Command.

php artisan make:controller Api/PostController --resource

Hier derr absolute Gewinner für alle schriebfaulen Entwickler. Du schreibst eine Zeile und bekommst Model mit dazugeöriger Migrationsdatei, Controller als ResourceController und gleich abgelegt im Apiordner. Mehr geht meiner Meinung bei Artisan nicht.

php artisan make:mail SuccessfullyRegistered

Erstellt Dir eine Mailfall. Im Ordner Mail, der beim erstmaligem verwenden angelegt wird.

php artisan make:component Alert

Erstellt Dir eine Laravel Component.

php artisan tinker

Öffnet dir die interne Laravel Console.

Und hier noch ein nicht artisan Befehl, dem man aber bestimmt häufiger braucht, wenn schon viele Tests geschrieben wurden und nicht will, dass alle jedes mal durchlaufen soll.

vendor/bin/phpunit --filter createFailedAreaFromGeoJsonBecauseEntryAllreadyExists

Laravel – Slugs einrichten

Slugs machen die URLs Sprechender und damit auch SEO-(Freund)licher. Statt domain.de/posts/1 würde man mit domain.de/posts/mein-erster-blog-eintrag arbeiten. Um das in Laravel zu realisieren, bedient man sich in der Regel externer Bibliotheken. Einmal eine von Spartie und eine cviebrock.

Man kann es natürlich aber auch selber machen. Die Bibliotheken sind hier aber vorzuziehen. Weil es ist keine große Logik dahinter und man spart einfach Zeit.

Ich werde hier die Bibliothek eloquent-sluggable von cviebrock nehmen.

1. Schritt:

composer require cviebrock/eloquent-sluggable 

2. Schritt

php artisan vendor:publish --provider="Cviebrock\EloquentSluggable\ServiceProvider"

3. Schritt

Wähle das entsprechende Model welches durch Slugs aufgerufen werden soll. In unserem Beispiel wäre es App\Models\Post.php (Laravel 8) und App\Post (Larvel < 8)

use Cviebrock\EloquentSluggable\Sluggable;
 
 
 class Post extends Model
 {
     use Sluggable;
 
 
     /**
      * Return the sluggable configuration array for this model.
      *
      * @return array
      */
     public function sluggable(): array
     {
         return [
             'slug' => [
                 'source' => 'title'
             ]
         ];
     }
 } 

In der Methode sluggable() definiert ihr was zu Slug gemacht werden soll. Bei Post (Artikeln) macht der Titel Sinn. Deshalb hier source => ‘title’.

So das war es auch fast schon. Ab jetzt werden beim Anlegen eines neuen Artikels der entsprechende Slug erzeugt und im Model mit abgespeichert. Falls ein Blog-Titel sich wiederholen sollte, würde das Sluggable das erkennen und mit einer Nummerierung hintern slug diesen eindeutig machen.

Um die Artikel jetzt auch mit Laravels Route-Model-Binding aufzurufen, müssen wir im Post Model die Methode

 public function getRouteKeyName()
 {
   return 'slug';
 }   

hinzufügen.

Diese Laravel Methode gibt an, den slug statt die id des Models zu nutzen.

Tailwind Purge unter npm run dev laufen lassen

Einer der Hauptgründe warum ich von Bootstrap auf TaiklwindCSS umgestiegen bin, ist der realisierte Traum von relative kleinen CSS Dateien. Statt ein ganzes CSS Framework immer und wieder zu laden, kann man mit TailwindCSS nur den wirklich im Code benötigten Style generieren lassen. Also eine 100% Effizienz, was bedeutet wir sparen Ressourcen. Und zwar Strom und Ladezeit (GCI – GreenCodeIdiology) So kann man CSS Datei Größen unter 40 KB erhalten und zwar für ein ganzes Projekt. Der Vollständigkeitshalber sei hier aber erwähnt, dass man Bootstrap bis zu einem gewissen Grad auch optimieren kann. Aber es kommt meiner Meinung bei Weitem nicht an die TailwindCSS Möglichkeiten herran.

Die Magie die hinter dem steckt, heißt bei Tailwind purge. Einstellungen zum purgen werden in der tailwind.config.js vorgenommen. Ich weiß es ist vielleicht überflüssig zu erwähnen, aber man kann nur purgen, wenn man Tailwind als Package geladen hat und nicht als CDN nutzt. Wie ihr euch TailwindCSS über ein Packagemanager installiert setze ich an dieser Stelle voraus.

Tailwind purged = bereinigt sobald es auf „Production“ geht. Also npm run prod. Bei einem npm run dev wird noch nicht gepurged. Wer das aber gerne möchte, kann es manuell in der tailwind.config.js einstellen.

module.exports = {
  purge: {
    enabled: true,
    content: [
      './src/**/*.html',
      './src/**/*.vue'
    ],
},

Und jetzt mal run dev laufen lassen und ihr werdet sehen, statt der üblichen 3,83 MB werden es erstaunlich weniger sein.

Laravel Blade Components

Componets – Aus VueJS kennen wir es und haben es bereits lieben gelernt. In Laravel können wir im Blade auch sehr dynamisch in Componenten arbeiten. Von Hause gibt uns Laravel Componets mit.

Am Beispiel erklärt

Stellen wir uns vor, wir wollen ein Blog erstellen. In der Übersichtsseite, sollen die Blogartikel untereinander gelistet werden. Wer ohne Componets gerabeitet hatte, wird in einer @foreach Schleife die Artikel ausgelesen haben und innerhalb der Blade Direktive @foreach ein HTML Block befüllt haben. In etwas so:

@foreach ($posts as $post)
  <div>
    <h1>$post->title</h1>
    <p>$post->description</p>
  </div>
@endforeach 

Wir können nun aber auch:

<x-postListing :posts=”$posts”/> 

schreiben.

Jetzt wird die Componente PostListing aufgerufen und die Posts Collection wird mit übergeben. Dafür legen wir die Componente PostListing mal an.

php artisan make:component PostListing 

Laravel Artisan legt uns zwei Dateien an.

1. app/View/Components/PostListing.php

2. views/components/postListing.blade.php

1. app/View/Components/PostListing.php

 use Illuminate\View\Component;
 
 
 class Card extends Component
 {
      * Create a new component instance.
      *
      * @return void
      */
     public function __construct()
     {}
 
 
     /**
      * Get the view / contents that represent the component.
      *
      * @return \Illuminate\Contracts\View\View|\Closure|string
      */
     public function render()
     {
         return view('components.card');
     }
 } 

2. views/components/postListing.blade.php

<div>
     <!-- Knowing is not enough; we must apply. Being willing is not enough; we must do. - Leonardo  
</div>

Nun müssen wir der Componente erklären, dass sie den Parameter posts erhalten wird. Das geschieht im Constructor der Klasse.

public function __construct($post)
{
  $this->post = $post;
} 

Und ab jetzt ist die Varaible ibn der PostListing Blade auch verfügbar. Deshalb können wir die Collection mit einer Foreach durchlaufen lassen. Also fügen wir in unsere Blade ein:

@foreach ($posts as $post)
 {{$post}}
 <x-posts :posts=”$post”/>
@endforeach

Nun können wir das ganze noch weiter gehen. Ich meine damit das wir eine weitere Componente dem ganzen hinzufügen. Und zwar die Componente Card. Jeder Post soll in einer Card im Listing dargestellt werden. Damit würden wir ein Grundstein für eine saubere Blade / View Architektur unser App legen. Zum Beispiel könnten wir mehre Carddesigns besitzen und diese werden im PostLinsting getriggert.

SeoTheater Autoren